{Version 9.43}
{*********************************************************}
{*                     FRAMBRWZ.PAS                      *}
{*              Copyright (c) 1995-2007 by               *}
{*                   L. David Baldwin                    *}
{*                 All rights reserved.                  *}
{*********************************************************}

{$i htmlcons.inc}

unit FramBrwz;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ExtCtrls, Menus, htmlsubs, htmlview, htmlun2,
  readHTML, FramView;

type
  TGetPostRequestEvent = procedure(Sender: TObject; IsGet: boolean; const URL, Query: string;
                       Reload: boolean; var NewURL: string; var DocType: ThtmlFileType;
                       var Stream: TMemoryStream) of Object;
  TGetPostRequestExEvent = procedure(Sender: TObject; IsGet: boolean;
                       const URL, Query, EncType, Referer: string;
                       Reload: boolean; var NewURL: string; var DocType: ThtmlFileType;
                       var Stream: TMemoryStream) of Object; 
  TbrFormSubmitEvent = procedure(Sender: TObject; Viewer: ThtmlViewer;
                     const Action, Target, EncType, Method: string;
                     Results: TStringList; var Handled: boolean) of Object;

  TbrFrameSet = class;
  TbrSubFrameSet = class;

  TbrFrameBase = class(TCustomPanel)   {base class for other classes}
    MasterSet: TbrFrameSet;   {Points to top (master) TbrFrameSet}
  private
    URLBase: string;
    UnLoaded: boolean;
    procedure UpdateFrameList; virtual; abstract;
  protected
    {$ifdef ver100_plus}  {Delphi 3,4,5, C++Builder 3, 4}
    LocalCharSet: TFontCharset;       
    {$endif}
    procedure FVMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); virtual; abstract;
    procedure FVMouseMove(Sender: TObject; Shift: TShiftState; X,
           Y: Integer); virtual; abstract;
    procedure FVMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); virtual; abstract;
    function CheckNoResize(var Lower, Upper: boolean): boolean; virtual; abstract;
    procedure LoadBrzFiles; virtual; abstract;
    procedure ReLoadFiles(APosition: LongInt); virtual; abstract;
    procedure UnloadFiles; virtual; abstract;

  public
    LOwner: TbrSubFrameSet;
    procedure InitializeDimensions(X, Y, Wid, Ht: integer); virtual; abstract;
  end;

  TbrFrame = class(TbrFrameBase) {TbrFrame holds a ThtmlViewer or TbrSubFrameSet}
  protected
    NoScroll: boolean;
    brMarginHeight, brMarginWidth: integer;    
    frHistory: TStringList;
    frPositionHistory: TFreeList;
    frHistoryIndex: integer;
    RefreshTimer: TTimer;     
    NextFile: string;      

    procedure CreateViewer;
    procedure frBumpHistory(const NewName: string; NewPos, OldPos: LongInt;
                   OldFormData: TFreeList);  
    procedure frBumpHistory1(const NewName: string; Pos: LongInt);
    procedure frSetHistoryIndex(Value: integer);
    procedure UpdateFrameList; override;
    procedure RefreshEvent(Sender: TObject; Delay: integer; const URL: string);
    procedure RefreshTimerTimer(Sender: TObject);

  protected
    procedure FVMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure FVMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer); override;
    procedure FVMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    function CheckNoResize(var Lower, Upper: boolean): boolean; override;
    procedure ReLoadFiles(APosition: LongInt); override;
    procedure UnloadFiles; override;
    procedure LoadBrzFiles; override;
    procedure frLoadFromBrzFile(const URL, Dest, Query, EncType, Referer: string;
              Bump, IsGet, Reload: boolean);  
    procedure ReloadFile(const FName: string; APosition: LongInt);
    procedure URLExpandName(Sender: TObject; const SRC: string; var Rslt: string); 
  public
    Viewer: ThtmlViewer;    {the ThtmlViewer it holds if any}
    ViewerPosition: LongInt;
    ViewerFormData: TFreeList;   
    FrameSet: TbrSubFrameSet; {or the TbrSubFrameSet it holds}
    Source,         {Dos filename or URL for this frame}
    OrigSource,      {Original Source name}  
    Destination: String;    {Destination offset for this frame}
    TheStream: TMemoryStream;
    TheStreamType: ThtmlFileType;
    WinName: String;     {window name, if any, for this frame}
    NoReSize: boolean;

    constructor CreateIt(AOwner: TComponent; L: TAttributeList;
              Master: TbrFrameSet; const Path: string);
    destructor Destroy; override;
    procedure InitializeDimensions(X, Y, Wid, Ht: integer); override;
    procedure Repaint; override;
    procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;  
  end;

  TbrSubFrameSet = class(TbrFrameBase)  {can contain one or more TbrFrames and/or TSubFrameSets}
  protected
    FBase: String;
    FBaseTarget: String;
    OuterBorder: integer;
    BorderSize: integer;
    FRefreshURL: string;
    FRefreshDelay: integer;
    RefreshTimer: TTimer;
    NextFile: string;

    procedure ClearFrameNames;
    procedure AddFrameNames;
    procedure UpdateFrameList; override;
    procedure HandleMeta(Sender: TObject; const HttpEq, Name, Content: string);
    procedure SetRefreshTimer;
    procedure RefreshTimerTimer(Sender: Tobject); virtual;
  protected
    OldRect: TRect;
    function GetRect: TRect;
    procedure FVMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure FVMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer); override;
    procedure FVMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure FindLineAndCursor(Sender: TObject; X, Y: integer);
    function NearBoundary(X, Y: integer): boolean;
    function CheckNoResize(var Lower, Upper: boolean): boolean; override;
    procedure Clear; virtual;

  public
    First: boolean;     {First time thru}
    Rows: boolean;          {set if row frameset, else column frameset}
    List: TFreeList;   {list of TbrFrames and TSubFrameSets in this TbrSubFrameSet}
    Dim,    {col width or row height as read.  Blanks may have been added}
    DimF,   {col width or row height in pixels as calculated and displayed}
    Lines   {pixel pos of lines, Lines[1]=0, Lines[DimCount]=width|height}
         : array[0..20] of SmallInt;
    Fixed   {true if line not allowed to be dragged}
         : array[0..20] of boolean;
    DimCount: integer;
    DimFTot: integer;
    LineIndex: integer;

    constructor CreateIt(AOwner: TComponent; Master: TbrFrameSet);
    destructor Destroy; override;
    function AddFrame(Attr: TAttributeList; const FName: string): TbrFrame;
    procedure EndFrameSet; virtual;
    procedure DoAttributes(L: TAttributeList);
    procedure LoadBrzFiles; override;
    procedure ReLoadFiles(APosition: LongInt); override;
    procedure UnloadFiles; override;
    procedure InitializeDimensions(X, Y, Wid, Ht: integer); override;
    procedure CalcSizes(Sender: TObject);
  end;

  TFrameBrowser = class;

  TbrFrameSet = class(TbrSubFrameSet)  {only one of these showing, others may be held as History}
  protected
    FTitle: String;
    FCurrentFile: String;
    FrameNames: TStringList; {list of Window names and their TFrames}
    Viewers: TList;   {list of all ThtmlViewer pointers}
    Frames: TList;    {list of all the Frames contained herein}
    HotSet: TbrFrameBase;     {owner of line we're moving}
    OldWidth, OldHeight: integer;
    NestLevel: integer;
    FActive: ThtmlViewer;   {the most recently active viewer}

    procedure ClearForwards;
    procedure UpdateFrameList; override;
    procedure RefreshTimerTimer(Sender: Tobject); override; 

  protected
    procedure FVMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer); override;
    procedure CheckActive(Sender: TObject);
    function GetActive: ThtmlViewer;
  public
    FrameViewer: TFrameBrowser;

    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure EndFrameSet; override;
    procedure LoadFromBrzFile(Stream: TMemoryStream; StreamType: ThtmlFileType;
                const URL, Dest: string);
    procedure Clear; override;
    procedure CalcSizes(Sender: TObject);
    procedure Repaint; override;
  end;

  TFrameBrowser = class(TFVBase)
  protected
    FPosition: TList;
    FHistoryIndex: integer;
    FOnGetPostRequest: TGetPostRequestEvent;
    FOnGetPostRequestEx: TGetPostRequestExEvent;
    FOnImageRequest: TGetImageEvent;
    FOptions: TFrameViewerOptions;
    FOnViewerClear: TNotifyEvent;
    InFormSubmit: boolean;
    FOnFormSubmit: TbrFormSubmitEvent;
    FEncodePostArgs: boolean;     
    FOnProgress: ThtProgressEvent;    

    FBaseEx: String;

    function GetBase: string;
    procedure SetBase(Value: string);
    function GetBaseTarget: string;
    function GetTitle: string;
    function GetCurrentFile: string;
    procedure HotSpotCovered(Sender: TObject; const SRC: string);
    procedure SetHistoryIndex(Value: integer);

    procedure ChkFree(Obj: TObject);
    function GetActiveTarget: string;
    function GetFwdButtonEnabled: boolean;
    function GetBackButtonEnabled: boolean;
    procedure SetOnImageRequest(const Value: TGetImageEvent);
    procedure SetOptions(Value: TFrameViewerOptions);
    procedure fvDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure fvDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure SetDragDrop(const Value: TDragDropEvent);
    procedure SetDragOver(const Value: TDragOverEvent);
    function GetViewers: TStrings;  override;
    procedure SetOnProgress(Handler: ThtProgressEvent);   

  protected
    CurbrFrameSet: TbrFrameSet;  {the TbrFrameSet being displayed}

    function GetCurViewerCount: integer; override;
    function GetCurViewer(I: integer): ThtmlViewer; override;
    function GetActiveViewer: ThtmlViewer;  override;


    procedure BumpHistory(OldFrameSet: TbrFrameSet; OldPos: LongInt);
    procedure BumpHistory1(const FileName, Title: string;
                 OldPos: LongInt; ft: ThtmlFileType);
    procedure BumpHistory2(OldPos: LongInt);
    function HotSpotClickHandled(const FullUrl: string): boolean;

    procedure AddFrame(FrameSet: TObject; Attr: TAttributeList; const FName: string); override;
    function CreateSubFrameSet(FrameSet: TObject): TObject; override;
    procedure DoAttributes(FrameSet: TObject; Attr: TAttributeList); override;
    procedure EndFrameSet(FrameSet: TObject); override;
    procedure AddVisitedLink(const S: string);
    procedure CheckVisitedLinks;
    procedure LoadURLInternal(const URL, Query, EncType, Referer: string; IsGet,
                Reload: boolean);
    procedure DoFormSubmitEvent(Sender: TObject; const Action, Target, EncType,
                Method: string;  Results: TStringList);
    procedure DoURLRequest(Sender: TObject; const SRC: string; var Stream: TMemoryStream);

  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Reload;
    procedure Clear;
    procedure HotSpotClick(Sender: TObject; const AnURL: string;
              var Handled: boolean);
    procedure ClearHistory; override;
    function ViewerFromTarget(const Target: string): ThtmlViewer;
    procedure GoBack;
    procedure GoFwd;
    procedure Repaint; override;
    procedure GetPostQuery(const URL, Query, EncType: string; IsGet: boolean);  
    procedure LoadURL(const URL: string);
    function GetViewerUrlBase(Viewer: ThtmlViewer): string;

    property Base: string read GetBase write SetBase;
    property BaseTarget: string read GetBaseTarget;
    property DocumentTitle: string read GetTitle;
    property CurrentFile: string read GetCurrentFile;
    property HistoryIndex: integer read FHistoryIndex write SetHistoryIndex;
    property EncodePostArgs: boolean read FEncodePostArgs write FEncodePostArgs;   

  published
    property FwdButtonEnabled: boolean read GetFwdButtonEnabled;
    property BackButtonEnabled: boolean read GetBackButtonEnabled;
    property OnGetPostRequest: TGetPostRequestEvent read FOnGetPostRequest write FOnGetPostRequest;
    property OnGetPostRequestEx: TGetPostRequestExEvent read FOnGetPostRequestEx write FOnGetPostRequestEx;
    property OnImageRequest: TGetImageEvent read FOnImageRequest
                      write SetOnImageRequest;
    property fvOptions: TFrameViewerOptions read FOptions write SetOptions
                 default [fvPrintTableBackground, fvPrintMonochromeBlack];
    property OnViewerClear: TNotifyEvent read FOnViewerClear write FOnViewerClear;
    property OnDragDrop: TDragDropEvent read FOnDragDrop write SetDragDrop;  
    property OnDragOver: TDragOverEvent read FOnDragOver write SetDragOver;
    property OnFormSubmit: TbrFormSubmitEvent read FOnFormSubmit write FOnFormSubmit; 
    property OnProgress: ThtProgressEvent read FOnProgress write SetOnProgress;   

  end;

implementation

uses
  UrlSubs;

const
  Sequence: integer = 10;

type
  PositionObj = class(TObject)
    Pos: LongInt;
    Seq: integer;
    FormData: TFreeList;    
    destructor Destroy; override;
    end;

function StreamToString(Stream: TStream): string;
var
  SL: TStringList;
begin
Result := '';
try
  SL := TStringList.Create;
  try
    SL.LoadFromStream(Stream);
    Result := SL.Text;
  finally
    Stream.Position := 0;  
    SL.Free;
    end;
except
  end;
end;

{----------------SplitURL}
procedure SplitURL(const Src: string; var FName, Dest: string);
{Split an URL into filename and Destination}
var
  I: integer;
begin
I := Pos('#', Src);
if I >= 1 then
  begin
  Dest := System.Copy(Src, I, Length(Src)-I+1);  {local destination}   
  FName := System.Copy(Src, 1, I-1);     {the file name}
  end
else
  begin
  FName := Src;
  Dest := '';    {no local destination}
  end;
end;

function ConvDosToHTML(const Name: string): string; forward;  

{----------------TbrFrame.CreateIt}
constructor TbrFrame.CreateIt(AOwner: TComponent; L: TAttributeList;
                   Master: TbrFrameSet; const Path: string);
var
  I: integer;
  S: string;
begin
inherited Create(AOwner);
{$ifdef ver100_plus}  {Delphi 3,4,5, C++Builder 3, 4}
if AOwner is TbrSubFrameSet then
  LocalCharSet := TbrSubFrameset(AOwner).LocalCharSet;
{$endif}
LOwner := AOwner as TbrSubFrameSet;
MasterSet := Master;
BevelInner := bvNone;
brMarginWidth := MasterSet.FrameViewer.MarginWidth;  
brMarginHeight := MasterSet.FrameViewer.MarginHeight;  
if LOwner.BorderSize = 0 then
  BevelOuter := bvNone
else
  begin
  BevelOuter := bvLowered;
  BevelWidth := LOwner.BorderSize;
  end;
ParentColor := True;
if Assigned(L) then
  for I := 0 to L.Count-1 do
    with TAttribute(L[I]) do
      case Which of
        SrcSy:
          begin
          SplitUrl(Trim(Name), S, Destination);
          S := ConvDosToHTML(S);
          if Pos(':/', S) <> 0 then
            URLBase := URLSubs.GetBase(S)   {get new base}
          else if ReadHTML.Base <> '' then
            begin
            S := Combine(ReadHTML.Base, S);
            URLBase := ReadHTML.Base;
            end
          else
            begin
            URLBase := LOwner.URLBase;
            S := Combine(URLBase, S);
            end;
          Source := S;
          OrigSource := S;     
          end;
        NameSy: WinName := Name;
        NoResizeSy:  NoResize := True;
        ScrollingSy:
          if CompareText(Name, 'NO') = 0 then  {auto and yes work the same}
            NoScroll := True;
        MarginWidthSy:  brMarginWidth := Value;   
        MarginHeightSy: brMarginHeight := Value;
        end;
if WinName <> '' then   {add it to the Window name list}
  (AOwner as TbrSubFrameSet).MasterSet.FrameNames.AddObject(Uppercase(WinName), Self);
OnMouseDown := FVMouseDown;
OnMouseMove := FVMouseMove;
OnMouseUp := FVMouseUp;
frHistory := TStringList.Create;
frPositionHistory := TFreeList.Create;
end;

{----------------TbrFrame.Destroy}
destructor TbrFrame.Destroy;
var
  I: integer;
begin
if Assigned(MasterSet) then
  begin
  if (WinName <> '')
      and Assigned(MasterSet.FrameNames) and MasterSet.FrameNames.Find(WinName, I)
      and (MasterSet.FrameNames.Objects[I] = Self) then
    MasterSet.FrameNames.Delete(I);
  if Assigned(Viewer) then
    begin
    if Assigned(MasterSet.Viewers) then
      MasterSet.Viewers.Remove(Viewer);
    if Assigned(MasterSet.Frames) then
      MasterSet.Frames.Remove(Self);
    if Viewer = MasterSet.FActive then MasterSet.FActive := Nil;
    end;
  end;
if Assigned(Viewer) then
  begin
  Viewer.Free;
  Viewer := Nil;
  end
else if Assigned(FrameSet) then
  begin
  FrameSet.Free;
  FrameSet := Nil;
  end;
frHistory.Free;  frHistory := Nil;
frPositionHistory.Free;  frPositionHistory := Nil;
ViewerFormData.Free;     
RefreshTimer.Free;
inherited Destroy;
end;

procedure TbrFrame.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);  
begin
inherited;
{in most cases, SetBounds results in a call to CalcSizes.  However, to make sure
 for case where there is no actual change in the bounds.... }
if Assigned(FrameSet) then
  FrameSet.CalcSizes(Nil);
end;

procedure TbrFrame.RefreshEvent(Sender: TObject; Delay: integer; const URL: string);
var
  Ext: string;
begin
if not (fvMetaRefresh in MasterSet.FrameViewer.FOptions) then
  Exit;
Ext := Lowercase(GetURLExtension(URL));
if (Ext = 'exe') or (Ext = 'zip') then Exit;
if URL = '' then
  NextFile := Source
else if not IsFullURL(URL) then
   NextFile := Combine(URLBase, URL)  //URLBase + URL
else
   NextFile := URL;
if not Assigned(RefreshTimer) then
  RefreshTimer := TTimer.Create(Self);
RefreshTimer.OnTimer := RefreshTimerTimer;
RefreshTimer.Interval := Delay*1000;
RefreshTimer.Enabled := True;
end;

procedure TbrFrame.RefreshTimerTimer(Sender: TObject);
var
  S, D: string;
begin
RefreshTimer.Enabled := False;
if Unloaded then Exit;
if not IsFullUrl(NextFile) then
  NextFile := Combine(UrlBase, NextFile);
if (MasterSet.Viewers.Count = 1) then    {load a new FrameSet}
  MasterSet.FrameViewer.LoadURLInternal(NextFile, '', '', '', True, True)
else
  begin
  SplitURL(NextFile, S, D);
  frLoadFromBrzFile(S, D, '', '', '', True, True, True);
  end;
end;

procedure TbrFrame.RePaint;
begin
if Assigned(Viewer) then Viewer.RePaint
else if Assigned(FrameSet) then FrameSet.RePaint;
inherited RePaint;
end;

{----------------TbrFrame.FVMouseDown}
procedure TbrFrame.FVMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
(Parent as TbrSubFrameSet).FVMouseDown(Sender, Button, Shift, X+Left, Y+Top);
end;

{----------------TbrFrame.FVMouseMove}
procedure TbrFrame.FVMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
if not NoResize then
  (Parent as TbrSubFrameSet).FVMouseMove(Sender, Shift, X+Left, Y+Top);
end;

{----------------TbrFrame.FVMouseUp}
procedure TbrFrame.FVMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
(Parent as TbrSubFrameSet).FVMouseUp(Sender, Button, Shift, X+Left, Y+Top);
end;

{----------------TbrFrame.CheckNoResize}
function TbrFrame.CheckNoResize(var Lower, Upper: boolean): boolean;
begin
Result := NoResize;
Lower := NoResize;
Upper := NoResize;
end;

{----------------TbrFrame.InitializeDimensions}
procedure TbrFrame.InitializeDimensions(X, Y, Wid, Ht: integer);
begin
if Assigned(FrameSet) then
  FrameSet.InitializeDimensions(X, Y, Wid, Ht);
end;

{----------------TbrFrame.CreateViewer}
procedure TbrFrame.CreateViewer;
begin
Viewer := ThtmlViewer.Create(Self);  {the Viewer for the frame}
Viewer.FrameOwner := Self;
Viewer.Width := ClientWidth;
Viewer.Height := ClientHeight;
Viewer.Align := alClient;
if (MasterSet.BorderSize = 0) or (fvNoFocusRect in MasterSet.FrameViewer.fvOptions) then  
  Viewer.BorderStyle := htNone;
Viewer.OnHotspotClick := LOwner.MasterSet.FrameViewer.HotSpotClick;
Viewer.OnHotspotCovered := LOwner.MasterSet.FrameViewer.HotSpotCovered;
if NoScroll then
  Viewer.Scrollbars := ssNone;
Viewer.DefBackground := MasterSet.FrameViewer.FBackground;
Viewer.Visible := False;
InsertControl(Viewer);
Viewer.SendToBack;
Viewer.Visible := True;
Viewer.Tabstop := True;
{$ifdef ver100_plus}  {Delphi 3,4,5, C++Builder 3, 4}
Viewer.CharSet := LocalCharset;
{$endif}
MasterSet.Viewers.Add(Viewer);
with MasterSet.FrameViewer do
  begin
  Viewer.ViewImages := FViewImages;
  Viewer.SetStringBitmapList(FBitmapList); 
  Viewer.ImageCacheCount := FImageCacheCount;
  Viewer.NoSelect := FNoSelect;
  Viewer.DefFontColor := FFontColor;
  Viewer.DefHotSpotColor := FHotSpotColor;
  Viewer.DefVisitedLinkColor := FVisitedColor;
  Viewer.DefOverLinkColor := FOverColor;
  Viewer.DefFontSize := FFontSize;
  Viewer.DefFontName := FFontName;
  Viewer.DefPreFontName := FPreFontName;
  Viewer.OnBitmapRequest := FOnBitmapRequest;
  if fvOverLinksActive in FOptions then
    Viewer.htOptions := Viewer.htOptions + [htOverLinksActive];
  if fvNoLinkUnderline in FOptions then
    Viewer.htOptions := Viewer.htOptions + [htNoLinkUnderline];
  if not (fvPrintTableBackground in FOptions) then
    Viewer.htOptions := Viewer.htOptions - [htPrintTableBackground];  
  if (fvPrintBackground in FOptions) then
    Viewer.htOptions := Viewer.htOptions + [htPrintBackground];  
  if not (fvPrintMonochromeBlack in FOptions) then
    Viewer.htOptions := Viewer.htOptions - [htPrintMonochromeBlack];
  if fvShowVScroll in FOptions then
    Viewer.htOptions := Viewer.htOptions + [htShowVScroll];
  if fvNoWheelMouse in FOptions then
    Viewer.htOptions := Viewer.htOptions + [htNoWheelMouse];  
  if Assigned(FOnImageRequest) then
    Viewer.OnImageRequest := FOnImageRequest;
  Viewer.OnFormSubmit := DoFormSubmitEvent;
  Viewer.OnLink := FOnLink;    
  Viewer.OnMeta := FOnMeta;
  Viewer.OnMetaRefresh := RefreshEvent;
  Viewer.OnRightClick := FOnRightClick;
  Viewer.OnProcessing := CheckProcessing;
  Viewer.OnMouseDown := OnMouseDown;
  Viewer.OnMouseMove := OnMouseMove;
  Viewer.OnMouseUp := OnMouseUp;
  Viewer.OnKeyDown := OnKeyDown;
  Viewer.OnKeyUp := OnKeyUp;
  Viewer.OnKeyPress := OnKeyPress;
  Viewer.Cursor := Cursor;
  Viewer.HistoryMaxCount := FHistoryMaxCount;
  Viewer.OnScript := FOnScript;
  Viewer.PrintMarginLeft := FPrintMarginLeft;
  Viewer.PrintMarginRight := FPrintMarginRight;
  Viewer.PrintMarginTop := FPrintMarginTop;
  Viewer.PrintMarginBottom := FPrintMarginBottom;
  Viewer.PrintScale := FPrintScale;   
  Viewer.OnPrintHeader := FOnPrintHeader;
  Viewer.OnPrintFooter := FOnPrintFooter;
  Viewer.OnPrintHtmlHeader := FOnPrintHtmlHeader;  
  Viewer.OnPrintHtmlFooter := FOnPrintHtmlFooter;
  Viewer.OnInclude := FOnInclude;
  Viewer.OnSoundRequest := FOnSoundRequest;
  Viewer.OnImageOver := FOnImageOver;
  Viewer.OnImageClick := FOnImageClick;
  Viewer.OnFileBrowse := FOnFileBrowse;  
  Viewer.OnObjectClick := FOnObjectClick;
  Viewer.OnObjectFocus := FOnObjectFocus;
  Viewer.OnObjectBlur := FOnObjectBlur;
  Viewer.OnObjectChange := FOnObjectChange;
  Viewer.ServerRoot := ServerRoot;
  Viewer.OnMouseDouble := FOnMouseDouble;
  Viewer.OnPanelCreate := FOnPanelCreate;
  Viewer.OnPanelDestroy := FOnPanelDestroy;
  Viewer.OnPanelPrint := FOnPanelPrint;   
  Viewer.OnDragDrop := fvDragDrop;
  Viewer.OnDragOver := fvDragOver;
  Viewer.OnParseBegin := FOnParseBegin;
  Viewer.OnParseEnd := FOnParseEnd;
  Viewer.OnProgress := FOnProgress;
  Viewer.OnObjectTag := OnObjectTag;   
  Viewer.OnhtStreamRequest := DoURLRequest;
  end;
Viewer.MarginWidth := brMarginWidth;    
Viewer.MarginHeight := brMarginHeight;  
Viewer.OnEnter := MasterSet.CheckActive;
Viewer.OnExpandName := UrlExpandName;    
end;

{----------------TbrFrame.LoadBrzFiles}
procedure TbrFrame.LoadBrzFiles;
var
  Item: TbrFrameBase;
  I: integer;
  Upper, Lower: boolean;
  Msg: string[255];
  NewURL: string;
  TheString: string;   
begin
if (Source <> '') and (MasterSet.NestLevel < 4) then
  begin
  if not Assigned(TheStream) then
    begin
    NewURL := '';
    if Assigned(MasterSet.FrameViewer.FOnGetPostRequestEx) then   
      MasterSet.FrameViewer.FOnGetPostRequestEX(Self, True, Source, '', '', '', False, NewURL, TheStreamType, TheStream)
    else
      MasterSet.FrameViewer.FOnGetPostRequest(Self, True, Source, '', False, NewURL, TheStreamType, TheStream);
    if NewURL <> '' then
      Source := NewURL;
    end;
  URLBase := GetBase(Source);
  Inc(MasterSet.NestLevel);
  try
    TheString := StreamToString(TheStream);  
    if (TheStreamType = HTMLType) and IsFrameString(LsString, '', TheString,
          MasterSet.FrameViewer) then
      begin
      FrameSet := TbrSubFrameSet.CreateIt(Self, MasterSet);
      FrameSet.Align := alClient;
      FrameSet.Visible := False;
      InsertControl(FrameSet);
      FrameSet.SendToBack;
      FrameSet.Visible := True;
      FrameParseString(MasterSet.FrameViewer, FrameSet, lsString, '', TheString, FrameSet.HandleMeta);
      Self.BevelOuter := bvNone;
      frBumpHistory1(Source, 0);
      with FrameSet do
        begin
        for I := 0 to List.Count-1 do
          Begin
          Item := TbrFrameBase(List.Items[I]);
          Item.LoadBrzFiles;
          end;
        CheckNoresize(Lower, Upper);
        if FRefreshDelay > 0 then
          SetRefreshTimer;
        end;
      end
    else
      begin
      CreateViewer;     
      Viewer.Base := MasterSet.FBase;
      Viewer.LoadStream(Source, TheStream, TheStreamType);
      Viewer.PositionTo(Destination);
      frBumpHistory1(Source, Viewer.Position);
      end;
  except
    if not Assigned(Viewer) then
      CreateViewer;
    if Assigned(FrameSet) then
      begin
      FrameSet.Free;
      FrameSet := Nil;
      end;
    Msg := '<p><img src="qw%&.bmp" alt="Error"> Can''t load '+Source;
    Viewer.LoadFromBuffer(@Msg[1], Length(Msg), '');  {load an error message}
    end;
  Dec(MasterSet.NestLevel);
  end
else
  begin  {so blank area will perform like the TFrameBrowser}
  OnMouseDown := MasterSet.FrameViewer.OnMouseDown;
  OnMouseMove := MasterSet.FrameViewer.OnMouseMove;
  OnMouseUp := MasterSet.FrameViewer.OnMouseUp;
  end;
end;

{----------------TbrFrame.ReloadFiles}
procedure TbrFrame.ReloadFiles(APosition: LongInt);
var
  Item: TbrFrameBase;
  I: integer;
  Upper, Lower: boolean;
  Dummy: string;

  procedure DoError;
  var
    Msg: string;
  begin
  Msg := '<p><img src="qw%&.bmp" alt="Error"> Can''t load '+Source;
  Viewer.LoadFromBuffer(@Msg[1], Length(Msg), '');  {load an error message}
  end;

begin
 if Source <> '' then
  if Assigned(FrameSet) then
    begin
    with FrameSet do
      begin
      for I := 0 to List.Count-1 do
        Begin
        Item := TbrFrameBase(List.Items[I]);
        Item.ReloadFiles(APosition);
        end;
      CheckNoresize(Lower, Upper);
      end;
    end
  else if Assigned(Viewer) then
    begin
    Viewer.Base := MasterSet.FBase;  {only effective if no Base to be read}
    try     
      if Assigned(MasterSet.FrameViewer.FOnGetPostRequestEx) then   
        MasterSet.FrameViewer.FOnGetPostRequestEx(Self, True, Source, '', '', '',False,
                                     Dummy, TheStreamType, TheStream)
      else
        MasterSet.FrameViewer.FOnGetPostRequest(Self, True, Source, '', False,
                                     Dummy, TheStreamType, TheStream);
      Viewer.LoadStream(Source, TheStream, TheStreamType);
      if APosition < 0 then
        Viewer.Position := ViewerPosition
      else Viewer.Position := APosition;    {its History Position}
      Viewer.FormData := ViewerFormData;   
      ViewerFormData.Free;
      ViewerFormData := Nil;      
    except
      DoError;
      end;
    end;
Unloaded := False;
end;

{----------------TbrFrame.UnloadFiles}
procedure TbrFrame.UnloadFiles;
var
  Item: TbrFrameBase;
  I: integer;
begin
if Assigned(RefreshTimer) then
  RefreshTimer.Enabled := False;
if Assigned(FrameSet) then
  begin
  with FrameSet do
    begin
    for I := 0 to List.Count-1 do
      Begin
      Item := TbrFrameBase(List.Items[I]);
      Item.UnloadFiles;
      end;
    end;
  end
else if Assigned(Viewer) then
  begin
  ViewerPosition := Viewer.Position;
  ViewerFormData := Viewer.FormData;    
  if Assigned(MasterSet.FrameViewer.FOnViewerClear) then
    MasterSet.FrameViewer.FOnViewerClear(Viewer);
  Viewer.Clear;
  if MasterSet.FActive = Viewer then
    MasterSet.FActive := Nil;             
  Viewer.OnSoundRequest := Nil;
  end;
Unloaded := True;
end;

{----------------TbrFrame.frLoadFromBrzFile}
procedure TbrFrame.frLoadFromBrzFile(const URL, Dest, Query, EncType, Referer: string; Bump, IsGet, Reload: boolean);
{URL is full URL here, has been seperated from Destination}
var
  OldPos: LongInt;
  HS, S, S1, OldTitle, OldName, OldBase: string;
  OldFormData: TFreeList;     
  SameName: boolean;
  OldViewer: ThtmlViewer;
  OldFrameSet: TbrSubFrameSet;
  TheString: string;   
  Upper, Lower, FrameFile: boolean;
  Item: TbrFrameBase;
  I: integer;

begin
if Assigned(RefreshTimer) then RefreshTimer.Enabled := False;
OldName := Source;
OldBase := URLBase;
S := URL;
if S = '' then S := OldName
else  URLBase := URLSubs.GetBase(S);   {get new base}
HS := S;
SameName := CompareText(S, OldName)= 0;
{if SameName, will not have to reload anything unless Reload set}

if not SameName or Reload then
  begin
  if Assigned(Viewer) and Assigned(MasterSet.FrameViewer.FOnViewerClear) then
    MasterSet.FrameViewer.FOnViewerClear(Viewer);
  S1 := '';
  if Assigned(MasterSet.FrameViewer.FOnGetPostRequestEx) then   
    MasterSet.FrameViewer.FOnGetPostRequestEx(Self, IsGet, S, Query, EncType, Referer, Reload, S1, TheStreamType, TheStream) 
  else
    MasterSet.FrameViewer.FOnGetPostRequest(Self, IsGet, S, Query, Reload, S1, TheStreamType, TheStream);
  if S1 <> '' then
    begin
    S := S1;
    URLBase := GetBase(S);
    end;
  end;
Source := S;

try
  TheString := StreamToString(TheStream);  
  if not SameName then
    try
      FrameFile := (TheStreamType = HTMLType) and
              IsFrameString(lsString, '', TheString, MasterSet.FrameViewer);
    except
      Raise(EfvLoadError.Create('Can''t load: '+URL));
      end
  else FrameFile := not Assigned(Viewer);
  if SameName and not Reload then
    if Assigned(Viewer) then
      begin
      OldPos := Viewer.Position;
      Viewer.PositionTo(Dest);
      MasterSet.FrameViewer.AddVisitedLink(URL+Dest);   
      if Bump and (Viewer.Position <> OldPos) then
        {Viewer to Viewer}
        frBumpHistory(HS, Viewer.Position, OldPos, Nil);
      end
    else
      begin
      with FrameSet do
        for I := 0 to List.Count-1 do
          Begin
          Item := TbrFrameBase(List.Items[I]);
          if (Item is TbrFrame) then
            with TbrFrame(Item) do
              if CompareText(Source, OrigSource) <> 0 then
                frLoadFromBrzFile(OrigSource, '', '', '', '', True, True, False);
          end;
      Exit;
      end      
  else if Assigned(Viewer) and not FrameFile then  {not samename or samename and reload}
    begin  {Viewer already assigned and it's not a Frame file}
    OldPos := Viewer.Position;
    OldTitle := Viewer.DocumentTitle;
    if Bump and not SameName and (MasterSet.Viewers.Count > 1) then  
      OldFormData := Viewer.FormData      
    else OldFormData := Nil;
    try
      Viewer.Base := MasterSet.FBase;
      Viewer.LoadStream(Source, TheStream, TheStreamType);
      if (Dest <> '') then
        Viewer.PositionTo(Dest);
      MasterSet.FrameViewer.AddVisitedLink(URL+Dest);
      if not samename then
        begin   {don't bump history on a forced reload}
        if MasterSet.Viewers.Count > 1 then
          begin
          if Bump then
             {Viewer to Viewer}
            frBumpHistory(HS, Viewer.Position, OldPos, OldFormData)   
          else OldFormData.Free;
          end
        else if (MasterSet.Viewers.Count = 1) and Bump then    
          {a single viewer situation, bump the history here}
          with MasterSet do
            begin
            FCurrentFile := Source;
            FTitle := Viewer.DocumentTitle;
            FBase := Viewer.Base;
            FBaseTarget := Viewer.BaseTarget;
            FrameViewer.BumpHistory1(OldName, OldTitle, OldPos, HTMLType);
            end;
        end;
    except
      OldFormData.Free;  
      Raise;
      end;
    end
  else
    begin {Viewer is not assigned or it is a Frame File}
    {keep the old viewer or frameset around (free later) to minimize blink}
    OldViewer := Viewer;  Viewer := Nil;
    OldFrameSet := FrameSet;  FrameSet := Nil;
    if OldFrameSet <> Nil then OldFrameSet.ClearFrameNames;
    if FrameFile then
      begin   {it's a frame file}
      FrameSet := TbrSubFrameSet.CreateIt(Self, MasterSet);
      FrameSet.URLBase := URLBase;
      FrameSet.Align := alClient;
      FrameSet.Visible := False;
      InsertControl(FrameSet);
      FrameSet.SendToBack;    {to prevent blink}
      FrameSet.Visible := True;
      FrameParseString(MasterSet.FrameViewer, FrameSet, lsString, '', TheString, FrameSet.HandleMeta);
      MasterSet.FrameViewer.AddVisitedLink(URL);
      Self.BevelOuter := bvNone;
      with FrameSet do
        begin
        for I := 0 to List.Count-1 do
          Begin
          Item := TbrFrameBase(List.Items[I]);
          Item.LoadBrzFiles;
          end;
        CheckNoresize(Lower, Upper);
        if FRefreshDelay > 0 then
          SetRefreshTimer;
        end;
      if Assigned(OldViewer) then
        frBumpHistory(HS, 0, OldViewer.Position, OldViewer.FormData)  
      else frBumpHistory(S, 0, 0, Nil);
      end
    else
      begin   {not a frame file but needs a viewer}
      CreateViewer;
      Viewer.Base := MasterSet.FBase;  
      Viewer.LoadStream(Source, TheStream, TheStreamType);
      Viewer.PositionTo(Dest);
      MasterSet.FrameViewer.AddVisitedLink(URL+Dest);
      {FrameSet to Viewer}
      frBumpHistory(HS, Viewer.Position, 0, Nil);
      end;
    if Assigned(FrameSet) then
      with FrameSet do
        begin
        with ClientRect do
          InitializeDimensions(Left, Top, Right-Left, Bottom-Top);
        CalcSizes(Nil);
        end;
    if Assigned(Viewer) then
      begin
      if MasterSet.BorderSize = 0 then
        BevelOuter := bvNone
      else
        begin
        BevelOuter := bvLowered;
        BevelWidth := MasterSet.BorderSize;
        end;
      if (Dest <> '') then
        Viewer.PositionTo(Dest);
      end;
    if Assigned(OldViewer) then
      begin
      MasterSet.Viewers.Remove(OldViewer);
      if MasterSet.FActive = OldViewer then
        MasterSet.FActive := Nil;
      OldViewer.Free;
      end
    else if Assigned(OldFrameSet) then
      begin
      OldFrameSet.UnloadFiles;
      OldFrameSet.Visible := False;
      end;
    RePaint;
    end;
  except
    Source := OldName;
    URLBase := OldBase;
    Raise;
    end;
end;

{----------------TbrFrame.ReloadFile}
procedure TbrFrame.ReloadFile(const FName: string; APosition: LongInt);
{It's known that there is only a single viewer, the file is not being changed,
 only the position}
begin
Viewer.Position := APosition;
end;

function ConvDosToHTML(const Name: string): string;    
{if Name is a Dos filename, convert it to HTML.  Add the file:// if it is
 a full pathe filename}
begin
Result := Name;
if Pos('\', Result) > 0 then
  begin
  Result := DosToHTML(Result);
  if (Pos('|', Result) > 0) then  {was something like c:\....}
    Result := 'file:///'+Result;
  end;
end;

{----------------TbrFrame.URLExpandName}    
procedure TbrFrame.URLExpandName(Sender: TObject; const SRC: string; var Rslt: string);
var
  S: string;
  Viewer: ThtmlViewer;
begin
S := ConvDosToHTML(SRC);  
if not IsFullUrl(S) then
  begin
  Viewer := Sender as ThtmlViewer;
  if Viewer.Base <> '' then      
    Rslt := Combine(GetBase(ConvDosToHTML(Viewer.Base)), S)
  else Rslt := Combine(UrlBase, S);
  end
else Rslt := S;
end;

{----------------TbrFrame.frBumpHistory}
procedure TbrFrame.frBumpHistory(const NewName: string;
              NewPos, OldPos: LongInt; OldFormData: TFreeList);
{applies to TFrames which hold a ThtmlViewer}{Viewer to Viewer}
var
  PO: PositionObj;
begin
with frHistory do
  begin
  if (Count > 0) then
    begin
    PositionObj(frPositionHistory[frHistoryIndex]).Pos := OldPos;
    if frHistory[frHistoryIndex] <> NewName then   
      PositionObj(frPositionHistory[frHistoryIndex]).FormData := OldFormData    
    else OldFormData.Free;
    end
  else OldFormData.Free;     
  MasterSet.ClearForwards;   {clear the history list forwards}
  frHistoryIndex := 0;
  InsertObject(0, NewName, FrameSet);  {FrameSet may be Nil here}
  PO := PositionObj.Create;
  PO.Pos := NewPos;
  PO.Seq := Sequence;
  Inc(Sequence);
  frPositionHistory.Insert(0, PO);
  MasterSet.UpdateFrameList;
  with MasterSet.FrameViewer do
    if Assigned(FOnHistoryChange) then
      FOnHistoryChange(MasterSet.FrameViewer);
  end;
end;

{----------------TbrFrame.frBumpHistory1}
procedure TbrFrame.frBumpHistory1(const NewName: string; Pos: LongInt);
{called from a fresh TbrFrame.  History list is empty}
var
  PO: PositionObj;
begin
with frHistory do
  begin
  frHistoryIndex := 0;
  InsertObject(0, NewName, FrameSet);  {FrameSet may be Nil here}
  PO := PositionObj.Create;
  PO.Pos := Pos;
  PO.Seq := Sequence;
  Inc(Sequence);
  frPositionHistory.Insert(0, PO);
  MasterSet.UpdateFrameList;     
  with MasterSet.FrameViewer do        
    if Assigned(FOnHistoryChange) then
      FOnHistoryChange(MasterSet.FrameViewer);
  end;
end;

{----------------TbrFrame.frSetHistoryIndex}
procedure TbrFrame.frSetHistoryIndex(Value: integer);
begin
with frHistory do
  if (Value <> frHistoryIndex) and (Value >= 0) and (Value < Count) then
    begin
    if Assigned(RefreshTimer) then
      RefreshTimer.Enabled := False;    {cut off any timing underway}
    if Assigned(Viewer) then   {current is Viewer}
      with PositionObj(frPositionHistory[frHistoryIndex]) do
        begin
        Pos := Viewer.Position;   {save the old position}
        {note that frHistoryIndex can only change by 1}
        PositionObj(frPositionHistory[frHistoryIndex]).FormData := Viewer.FormData;     
        end
    else
      begin    {Current is FrameSet}
      FrameSet.UnloadFiles;
      FrameSet.DestroyHandle;
      FrameSet.ClearFrameNames;
      FrameSet.Visible := False;
      FrameSet := Nil;   {it's not destroyed,though}
      end;

    if Objects[Value] is TbrSubFrameSet then
      begin
      FrameSet := TbrSubFrameSet(Objects[Value]);
      FrameSet.Visible := True;
      FrameSet.ReloadFiles(-1);
      FrameSet.AddFrameNames;
      if Assigned(Viewer) then
        begin
        if Assigned(MasterSet.Viewers) then
          MasterSet.Viewers.Remove(Viewer);
        if MasterSet.FActive = Viewer then
          MasterSet.FActive := Nil;
        Viewer.Free;
        Viewer := Nil;
        end;
      end
    else
      begin
      if not Assigned(Viewer) then
        CreateViewer;
      with PositionObj(frPositionHistory[Value]) do
        begin
        if (Source <> Strings[Value]) then
        frLoadFromBrzFile(Strings[Value], '', '', '', '', False, True, False);
        Viewer.FormData := FormData;   
        FormData.Free;
        FormData := Nil;
        Viewer.Position := Pos;
        end;
      end;
    Source := Strings[Value];
    frHistoryIndex := Value;
    MasterSet.UpdateFrameList;
    with MasterSet.FrameViewer do
      if Assigned(FOnHistoryChange) then
        FOnHistoryChange(MasterSet.FrameViewer);
    MasterSet.FrameViewer.CheckVisitedLinks;
    end;
end;

{----------------TbrFrame.UpdateFrameList}
procedure TbrFrame.UpdateFrameList;  
begin
MasterSet.Frames.Add(Self);
if Assigned(FrameSet) then
  FrameSet.UpdateFrameList;
end;

{----------------TbrSubFrameSet.CreateIt}
constructor TbrSubFrameSet.CreateIt(AOwner: TComponent; Master: TbrFrameSet);
begin
inherited Create(AOwner);
MasterSet := Master;
{$ifdef ver100_plus}  {Delphi 3,4,5, C++Builder 3, 4}
if AOwner is TbrFrameBase then
  LocalCharSet := TbrSubFrameset(AOwner).LocalCharSet;       
{$endif}
OuterBorder := 0;   {no border for subframesets}  
if Self <> Master then
  BorderSize := Master.BorderSize;   
First := True;
List := TFreeList.Create;
FBase := '';
FBaseTarget := '';
OnResize := CalcSizes;
OnMouseDown := FVMouseDown;
OnMouseMove := FVMouseMove;
OnMouseUp := FVMouseUp;
{$ifdef delphi7_plus}
ParentBackground := False;
{$endif}
ParentColor := True;
if (AOwner is TbrFrameBase) then
  URLBase := TbrFrameBase(AOwner).URLBase;   
end;

{----------------TbrSubFrameSet.ClearFrameNames}
procedure TbrSubFrameSet.ClearFrameNames;
var
  I, J: integer;
begin
for J := 0 to List.Count-1 do
  if (TbrFrameBase(List[J]) is TbrFrame) then
    begin  
    with TbrFrame(List[J]) do
      if Assigned(MasterSet) and (WinName <> '')
            and Assigned(MasterSet.FrameNames)
              and MasterSet.FrameNames.Find(WinName, I) then
                MasterSet.FrameNames.Delete(I);
    end
  else if (TbrFrameBase(List[J]) is TbrSubFrameSet) then
    TbrSubFrameSet(List[J]).ClearFrameNames;
end;

{----------------TbrSubFrameSet.AddFrameNames}
procedure TbrSubFrameSet.AddFrameNames;         
var
  J: integer;
  Frame: TbrFrame;
begin
for J := 0 to List.Count-1 do
  if (TbrFrameBase(List[J]) is TbrFrame) then
    begin
    Frame := TbrFrame(List[J]);
    with Frame do
      if Assigned(MasterSet) and (WinName <> '')
            and Assigned(MasterSet.FrameNames) then
              begin
              MasterSet.FrameNames.AddObject(Uppercase(WinName), Frame);
              end;
    end
  else if (TbrFrameBase(List[J]) is TbrSubFrameSet) then
    TbrSubFrameSet(List[J]).AddFrameNames;
end;

{----------------TbrSubFrameSet.Destroy}
destructor TbrSubFrameSet.Destroy;
begin
List.Free;
List := Nil;
RefreshTimer.Free;
inherited Destroy;
end;

{----------------TbrSubFrameSet.AddFrame}
function TbrSubFrameSet.AddFrame(Attr: TAttributeList; const FName: string): TbrFrame;
{called by the parser when <Frame> is encountered within the <Frameset>
 definition}
begin
Result := TbrFrame.CreateIt(Self, Attr, MasterSet, ExtractFilePath(FName));
List.Add(Result);
Result.SetBounds(OuterBorder, OuterBorder, Width-2*OuterBorder, Height-2*OuterBorder);  
InsertControl(Result);
end;

{----------------TbrSubFrameSet.DoAttributes}
procedure TbrSubFrameSet.DoAttributes(L: TAttributeList);
{called by the parser to process the <Frameset> attributes}
var
  T: TAttribute;
  S: string;
  Numb: string[20];

  procedure GetDims;
  const
    EOL = ^M;
  var
    Ch: char;
    I, N: integer;

    procedure GetCh;
    begin
    if I > Length(S) then Ch := EOL
    else
      begin
      Ch := S[I];
      Inc(I);
      end;
    end;

  begin
  if Name = '' then S := T.Name
  else Exit;
  I := 1;   DimCount := 0;
  repeat
    Inc(DimCount);
    Numb := '';
    GetCh;
    while not (Ch in ['0'..'9', '*', EOL, ',']) do GetCh;
    if Ch in ['0'..'9'] then
      begin
      while Ch in ['0'..'9'] do
        begin
        Numb := Numb+Ch;
        GetCh;
        end;
      N := IntMax(1, StrToInt(Numb));   {no zeros}
      while not (Ch in ['*', '%', ',', EOL]) do GetCh;
      if ch = '*' then
        begin
        Dim[DimCount] := -IntMin(99, N);{store '*' relatives as negative, -1..-99}
        GetCh;
        end
      else if Ch = '%' then
        begin  {%'s stored as -(100 + %),  i.e. -110 is 10% }
        Dim[DimCount] := -IntMin(1000, N+100);  {limit to 900%}
        GetCh;
        end
      else Dim[DimCount] := IntMin(N, 5000);  {limit absolute to 5000}
      end
    else if Ch in ['*', ',', EOL] then
      begin
      Dim[DimCount] := -1;
      if Ch = '*' then GetCh;
      end;
    while not (Ch in [',', EOL]) do GetCh;
  until (Ch = EOL) or (DimCount = 20);
  end;

begin
{read the row or column widths into the Dim array}
If L.Find(RowsSy, T) then
  begin
  Rows := True;
  GetDims;
  end;
if L.Find(ColsSy, T) and (DimCount <=1) then
  begin
  Rows := False;
  DimCount := 0;
  GetDims;
  end;
if (Self = MasterSet) and not (fvNoBorder in MasterSet.FrameViewer.FOptions) then
                               {BorderSize already defined as 0}  
  if L.Find(BorderSy, T) or L.Find(FrameBorderSy, T)then
    begin
    BorderSize := T.Value;
    OuterBorder := IntMax(2-BorderSize, 0);
    if OuterBorder >= 1 then
      begin
      BevelWidth := OuterBorder;
      BevelOuter := bvLowered;
      end;
    end
  else BorderSize := 2;  
end;

{----------------TbrSubFrameSet.LoadBrzFiles}
procedure TbrSubFrameSet.LoadBrzFiles;
var
  I: integer;
  Item: TbrFrameBase;
begin
for I := 0 to List.Count-1 do
  begin
  Item := TbrFrameBase(List.Items[I]);
  Item.LoadBrzFiles;
  end;
end;

{----------------TbrSubFrameSet.ReloadFiles}
procedure TbrSubFrameSet.ReloadFiles(APosition: LongInt);
var
  I: integer;
  Item: TbrFrameBase;
begin
for I := 0 to List.Count-1 do
  begin
  Item := TbrFrameBase(List.Items[I]);
  Item.ReloadFiles(APosition);
  end;
if (FRefreshDelay > 0) and Assigned(RefreshTimer) then
  SetRefreshTimer;
Unloaded := False;
end;

{----------------TbrSubFrameSet.UnloadFiles}
procedure TbrSubFrameSet.UnloadFiles;
var
  I: integer;
  Item: TbrFrameBase;
begin
if Assigned(RefreshTimer) then
  RefreshTimer.Enabled := False;
for I := 0 to List.Count-1 do
  begin
  Item := TbrFrameBase(List.Items[I]);
  Item.UnloadFiles;
  end;
if Assigned(MasterSet.FrameViewer.FOnSoundRequest) then
  MasterSet.FrameViewer.FOnSoundRequest(MasterSet, '', 0, True);
Unloaded := True;
end;

{----------------TbrSubFrameSet.EndFrameSet}
procedure TbrSubFrameSet.EndFrameSet;
{called by the parser when </FrameSet> is encountered}
var
  I: integer;
begin
if List.Count > DimCount then  {a value left out}
  begin  {fill in any blanks in Dim array}
  for I := DimCount+1 to List.Count do
    begin
    Dim[I] := -1;      {1 relative unit}
    Inc(DimCount);
    end;
  end
else while DimCount > List.Count do  {or add Frames if more Dims than Count}
  AddFrame(Nil, '');
if ReadHTML.Base <> '' then      
  FBase := ReadHTML.Base
else FBase := MasterSet.FrameViewer.FBaseEx;
FBaseTarget := ReadHTML.BaseTarget;
end;

{----------------TbrSubFrameSet.InitializeDimensions}
procedure TbrSubFrameSet.InitializeDimensions(X, Y, Wid, Ht: integer);
var
  I, Total, PixTot, PctTot, RelTot, Rel, Sum,
  Remainder, PixDesired, PixActual: integer;

begin
if Rows then
  Total := Ht
else Total := Wid;
PixTot := 0;  RelTot := 0;  PctTot := 0; DimFTot := 0;
for I := 1 to DimCount do   {count up the total pixels, %'s and relatives}
  if Dim[I] >= 0 then
    PixTot := PixTot + Dim[I]
  else if Dim[I] <= -100 then
    PctTot :=  PctTot + (-Dim[I]-100)
  else RelTot := RelTot - Dim[I];
Remainder := Total - PixTot;
if Remainder <= 0 then
  begin    {% and Relative are 0, must scale absolutes}
  for I := 1 to DimCount do
    begin
    if Dim[I] >= 0 then
      DimF[I] := MulDiv(Dim[I], Total, PixTot)  {reduce to fit}
    else DimF[I] := 0;
    Inc(DimFTot, DimF[I]);
    end;
  end
else    {some remainder left for % and relative}
  begin
  PixDesired := MulDiv(Total, PctTot, 100);
  if PixDesired > Remainder then
    PixActual := Remainder
  else PixActual := PixDesired;
  Dec(Remainder, PixActual);   {Remainder will be >= 0}
  if RelTot > 0 then
    Rel := Remainder div RelTot  {calc each relative unit}
  else Rel := 0;
  for I := 1 to DimCount do  {calc the actual pixel widths (heights) in DimF}
    begin
    if Dim[I] >= 0 then
      DimF[I] := Dim[I]
    else if Dim[I] <= -100 then
      DimF[I] := MulDiv(-Dim[I]-100, PixActual, PctTot)
    else DimF[I] := -Dim[I] * Rel;
    Inc(DimFTot, DimF[I]);
    end;
  end;

Sum := 0;
for I := 0 to List.Count-1 do  {intialize the dimensions of contained items}
  begin
  if Rows then
    TbrFrameBase(List.Items[I]).InitializeDimensions(X, Y+Sum, Wid, DimF[I+1])
  else
    TbrFrameBase(List.Items[I]).InitializeDimensions(X+Sum, Y, DimF[I+1], Ht);
  Sum := Sum+DimF[I+1];
  end;
end;

{----------------TbrSubFrameSet.CalcSizes}
{OnResize event comes here}
procedure TbrSubFrameSet.CalcSizes(Sender: TObject);
var
  I, Step, Sum, ThisTotal: integer;
  ARect: TRect;
begin
{Note: this method gets called during Destroy as it's in the OnResize event.
 Hence List may be Nil.}
if Assigned(List) and (List.Count > 0) then
  begin
  ARect := ClientRect;
  InflateRect(ARect, -OuterBorder, -OuterBorder);      
  Sum := 0;
  if Rows then ThisTotal := ARect.Bottom - ARect.Top
  else ThisTotal := ARect.Right-ARect.Left;
  for I := 0 to List.Count-1 do
    begin
    Step := MulDiv(DimF[I+1], ThisTotal, DimFTot);
    if Rows then
      TbrFrameBase(List.Items[I]).SetBounds(ARect.Left, ARect.Top+Sum, ARect.Right-ARect.Left, Step)
    else
      TbrFrameBase(List.Items[I]).SetBounds(ARect.Left+Sum, ARect.Top, Step, ARect.Bottom-Arect.Top);
    Sum := Sum+Step;
    Lines[I+1] := Sum;
    end;
  end;
end;

{----------------TbrSubFrameSet.NearBoundary}
function TbrSubFrameSet.NearBoundary(X, Y: integer): boolean;
begin
Result := (Abs(X) < 4) or (Abs(X - Width) < 4) or
             (Abs(Y) < 4) or (Abs(Y-Height) < 4);
end;

{----------------TbrSubFrameSet.GetRect}
function TbrSubFrameSet.GetRect: TRect;
{finds the FocusRect to draw when draging boundaries}
var
  Pt, Pt1, Pt2: TPoint;
begin
Pt1 := Point(0, 0);
Pt1 := ClientToScreen(Pt1);
Pt2 := Point(ClientWidth, ClientHeight);
Pt2 := ClientToScreen(Pt2);
GetCursorPos(Pt);
if Rows then
  Result := Rect(Pt1.X, Pt.Y-1, Pt2.X, Pt.Y+1)
else
  Result := Rect(Pt.X-1, Pt1.Y, Pt.X+1, Pt2.Y);
OldRect := Result;
end;

{----------------DrawRect}
procedure DrawRect(ARect: TRect);
{Draws a Focus Rect}
var
  DC: HDC;
begin
DC := GetDC(0);
DrawFocusRect(DC, ARect);
ReleaseDC(0, DC);
end;

{----------------TbrSubFrameSet.FVMouseDown}
procedure TbrSubFrameSet.FVMouseDown(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);
var
  ACursor: TCursor;
  RP: record
      case boolean of
        True: (P1, P2: TPoint);
        False:(R: TRect);
        end;
begin
if Button <> mbLeft then Exit;
if NearBoundary(X, Y) then
  begin
  if Parent is TbrFrameBase then
    (Parent as TbrFrameBase).FVMouseDown(Sender, Button, Shift, X+Left, Y+Top)
  else
    Exit;
  end
else
  begin
  ACursor := (Sender as TbrFrameBase).Cursor;
  if (ACursor = crVSplit) or(ACursor = crHSplit) then
    begin
    MasterSet.HotSet := Self;
    with RP do
      begin   {restrict cursor to lines on both sides}
      if Rows then
        R := Rect(0, Lines[LineIndex-1]+1, ClientWidth, Lines[LineIndex+1]-1)
      else
        R := Rect(Lines[LineIndex-1]+1, 0, Lines[LineIndex+1]-1, ClientHeight);
      P1 := ClientToScreen(P1);
      P2 := ClientToScreen(P2);
      ClipCursor(@R);
      end;
    DrawRect(GetRect);
    end;
  end;
end;

{----------------TbrSubFrameSet.FindLineAndCursor}
procedure TbrSubFrameSet.FindLineAndCursor(Sender: TObject; X, Y: integer);
var
  ACursor: TCursor;
  Gap, ThisGap, Line, I: integer;
begin
if  not Assigned(MasterSet.HotSet) then
  begin  {here we change the cursor as mouse moves over lines,button up or down}
  if Rows then Line := Y else Line := X;
  Gap := 9999;
  for I := 1 to DimCount-1 do
    begin
    ThisGap := Line-Lines[I];
    if Abs(ThisGap) < Abs(Gap) then
      begin
      Gap := Line - Lines[I];
      LineIndex := I;
      end
    else if Abs(ThisGap) = Abs(Gap) then  {happens if 2 lines in same spot}
      if ThisGap >= 0 then  {if Pos, pick the one on right (bottom)}
        LineIndex := I;
    end;

  if (Abs(Gap) <= 4) and not Fixed[LineIndex] then
    begin
    if Rows then
      ACursor := crVSplit
    else ACursor := crHSplit;
    (Sender as TbrFrameBase).Cursor := ACursor;
    end
  else (Sender as TbrFrameBase).Cursor := MasterSet.FrameViewer.Cursor;
  end
else
  with TbrSubFrameSet(MasterSet.HotSet) do
    begin
    DrawRect(OldRect);
    DrawRect(GetRect);
    end;
end;

{----------------TbrSubFrameSet.FVMouseMove}
procedure TbrSubFrameSet.FVMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
if NearBoundary(X, Y) then
  (Parent as TbrFrameBase).FVMouseMove(Sender, Shift, X+Left, Y+Top)
else
  FindLineAndCursor(Sender, X, Y);
end;

{----------------TbrSubFrameSet.FVMouseUp}
procedure TbrSubFrameSet.FVMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer);
var
  I: integer;
begin
if Button <> mbLeft then Exit;
if MasterSet.HotSet = Self then
    begin
    MasterSet.HotSet := Nil;
    DrawRect(OldRect);
    ClipCursor(Nil);
    if Rows then
      Lines[LineIndex] := Y else Lines[LineIndex] := X;
    for I := 1 to DimCount do
      if I = 1 then DimF[1] := MulDiv(Lines[1], DimFTot, Lines[DimCount])
      else DimF[I] := MulDiv((Lines[I] - Lines[I-1]), DimFTot, Lines[DimCount]);
    CalcSizes(Self);
    Invalidate;           
    end
else if (Parent is TbrFrameBase) then  
  (Parent as TbrFrameBase).FVMouseUp(Sender, Button, Shift, X+Left, Y+Top);
end;

{----------------TbrSubFrameSet.CheckNoResize}
function TbrSubFrameSet.CheckNoResize(var Lower, Upper: boolean): boolean;
var
  Lw, Up: boolean;
  I: integer;
begin
Result := False; Lower := False;  Upper := False;
for I := 0 to List.Count-1 do
  with TbrFrameBase(List[I]) do
    if CheckNoResize(Lw, Up) then
      begin
      Result := True;  {sides are fixed}
      Fixed[I] := True;  {these edges are fixed}
      Fixed[I+1] := True;
      If Lw and (I = 0) then Lower := True;
      If Up and (I = List.Count-1) then Upper := True;
      end;
end;

{----------------TbrSubFrameSet.Clear}
procedure TbrSubFrameSet.Clear;
var
  I: integer;
  X: TbrFrameBase;
begin
for I := List.Count-1 downto 0 do
  begin
  X := List.Items[I];
  List.Delete(I);
  RemoveControl(X);
  X.Free;
  end;
DimCount := 0;
First := True;
Rows := False;
FillChar(Fixed, Sizeof(Fixed), 0);
FillChar(Lines, Sizeof(Lines), 0);
FBase := '';
FBaseTarget := '';
end;

{----------------TbrSubFrameSet.UpdateFrameList}
procedure TbrSubFrameSet.UpdateFrameList;
var
  I: integer;
begin
for I := 0 to List.Count-1 do
  TbrFrameBase(List[I]).UpdateFrameList;
end;

{----------------TbrSubFrameSet.HandleMeta}
procedure TbrSubFrameSet.HandleMeta(Sender: TObject; const HttpEq, Name, Content: string);  
var
  DelTime, I: integer;
begin
{$ifdef ver100_plus}  {Delphi 3,4,5, C++Builder 3, 4}
if CompareText(HttpEq, 'content-type') = 0 then
  TranslateCharset(Content, LocalCharset);     
{$endif}

with MasterSet.FrameViewer do
  begin
  if Assigned(FOnMeta) then FOnMeta(Sender, HttpEq, Name, Content);
  if not (fvMetaRefresh in FOptions) then Exit;
  end;

if CompareText(Lowercase(HttpEq), 'refresh') = 0 then
  begin
  I := Pos(';', Content);
  if I > 0 then                                        
    DelTime := StrToIntDef(copy(Content, 1, I-1), -1)
  else DelTime := StrToIntDef(Content, -1);
  if DelTime < 0 then Exit
  else if DelTime = 0 then DelTime := 1;   
  I := Pos('url=', Lowercase(Content));
  if I > 0 then
    FRefreshURL := Copy(Content, I+4, Length(Content)-I-3)
  else FRefreshURL := '';
  FRefreshDelay := DelTime;
  end;
end;

{----------------TbrSubFrameSet.SetRefreshTimer}
procedure TbrSubFrameSet.SetRefreshTimer;
begin
NextFile := FRefreshURL;
if not Assigned(RefreshTimer) then
  RefreshTimer := TTimer.Create(Self);
RefreshTimer.OnTimer := RefreshTimerTimer;
RefreshTimer.Interval := FRefreshDelay*1000;
RefreshTimer.Enabled := True;
end;

{----------------TbrSubFrameSet.RefreshTimerTimer}
procedure TbrSubFrameSet.RefreshTimerTimer(Sender: Tobject);  
var
  S, D: string;
begin
RefreshTimer.Enabled := False;
if Unloaded then Exit;
if Owner is TbrFrame then
  begin
  SplitURL(NextFile, S, D);
  TbrFrame(Owner).frLoadFromBrzFile(S, D, '', '', '', True, True, True)
  end;
end;

{----------------TbrFrameSet.Create}
constructor TbrFrameSet.Create(AOwner: TComponent);
begin
inherited CreateIt(AOwner, Self);
FrameViewer := AOwner as TFrameBrowser;
{$ifdef ver100_plus}  {Delphi 3,4,5, C++Builder 3, 4}
LocalCharSet := FrameViewer.FCharset;       
{$endif}
if fvNoBorder in FrameViewer.FOptions then
  BorderSize := 0    
else
  BorderSize := 2;
BevelOuter := bvNone;
FTitle := '';
FrameNames := TStringList.Create;
FrameNames.Sorted := True;
Viewers := TList.Create;
Frames := TList.Create;
OnResize := CalcSizes;
end;

{----------------TbrFrameSet.Destroy}
destructor TbrFrameSet.Destroy;
begin
FrameNames.Free;
FrameNames := Nil;   {is tested later}
Viewers.Free;
Viewers := Nil;
Frames.Free;    
Frames := Nil;
inherited Destroy;
end;

{----------------TbrFrameSet.Clear}
procedure TbrFrameSet.Clear;
begin
inherited Clear;
FrameNames.Clear;
Viewers.Clear;
Frames.Clear;   
HotSet := Nil;
FTitle := '';
FCurrentFile:= '';
OldHeight := 0;
OldWidth := 0;
FActive := Nil;
end;

procedure TbrFrameSet.RePaint;     
var
  I: integer;
begin
if Assigned(Frames) then
  for I := 0 to Frames.Count-1 do
    TWinControl(Frames[I]).RePaint;
inherited;     
end;

{----------------TbrFrameSet.EndFrameSet}
procedure TbrFrameSet.EndFrameSet;
begin
FTitle := ReadHTML.Title;
inherited EndFrameSet;
with ClientRect do
  InitializeDimensions(Left, Top, Right-Left, Bottom-Top);
end;

{----------------TbrFrameSet.CalcSizes}
{OnResize event comes here}
procedure TbrFrameSet.CalcSizes(Sender: TObject);
var
  ARect: TRect;
begin
ARect := ClientRect;
InflateRect(ARect, -OuterBorder, -OuterBorder);
with ARect do
  begin
  if (OldWidth <> Right-Left) or (OldHeight <> Bottom-Top) then
    begin
    InitializeDimensions(Left, Top, Right-Left, Bottom-Top);
    inherited CalcSizes(Sender);
    end;
  OldWidth := Right-Left;
  OldHeight := Bottom-Top;
  end;
end;

{----------------TbrFrameSet.CheckActive}
procedure TbrFrameSet.CheckActive(Sender: TObject);
begin
if Sender is ThtmlViewer then
  FActive := ThtmlViewer(Sender);
end;

{----------------TbrFrameSet.GetActive}
function TbrFrameSet.GetActive: ThtmlViewer;
begin
if Viewers.Count = 1 then
  Result := ThtmlViewer(Viewers[0])
else
  try
    if FActive is ThtmlViewer then Result := FActive
      else Result := Nil;
  except
    Result := Nil;
  end;
end;

{----------------TbrFrameSet.FVMouseMove}
procedure TbrFrameSet.FVMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
FindLineAndCursor(Sender, X, Y);
if (LineIndex = 0) or (LineIndex = DimCount) then
  begin    {picked up the outer boundary}
  (Sender as TbrFrameBase).Cursor := MasterSet.FrameViewer.Cursor;
  Cursor := MasterSet.FrameViewer.Cursor;
  end;
end;

procedure TbrFrameSet.RefreshTimerTimer(Sender: Tobject);
begin
RefreshTimer.Enabled := False;
if (Self = MasterSet.FrameViewer.CurbrFrameSet) then
FrameViewer.LoadURLInternal(NextFile, '', '', '', True, True)
end;

{----------------TbrFrameSet.LoadFromBrzFile}
procedure TbrFrameSet.LoadFromBrzFile(Stream: TMemoryStream; StreamType: ThtmlFileType;
               const URL, Dest: string);
var
  I: integer;
  Item: TbrFrameBase;
  Frame: TbrFrame;
  Lower, Upper: boolean;
  TheString: string;
begin
Clear;
NestLevel := 0;
FCurrentFile := URL;
TheString := StreamToString(Stream);  
if (StreamType = HTMLType) and
      IsFrameString(lsString, '', TheString, MasterSet.FrameViewer) then
  begin    {it's a Frameset html file}
  FrameParseString(FrameViewer, Self, lsString, '', TheString, HandleMeta);  
  for I := 0 to List.Count-1 do
    Begin
    Item := TbrFrameBase(List.Items[I]);
    TbrFrameBase(Item).LoadBrzFiles;
    end;
  CalcSizes(Self);
  CheckNoresize(Lower, Upper);
  if FRefreshDelay > 0 then
    SetRefreshTimer;
  end
else
  begin   {it's a non frame file}
  Frame := AddFrame(Nil, '');
  Frame.Source := URL;
  Frame.TheStream := Stream;
  Frame.TheStreamType := StreamType;
  Frame.Destination := Dest;
  EndFrameSet;
  CalcSizes(Self);
  Frame.LoadBrzFiles;
  FTitle := ReadHTML.Title;
  FBase := ReadHTML.Base;       
  FBaseTarget := ReadHTML.BaseTarget;
  end;
end;

{----------------TbrFrameSet.ClearForwards}
procedure TbrFrameSet.ClearForwards;
{clear all the forward items in the history lists}
var
  I, J: integer;
  Frame: TbrFrame;
  AList: TList;
  Obj: TObject;
begin
AList := TList.Create;
for J := 0 to Frames.Count-1 do
  begin
  Frame := TbrFrame(Frames[J]);
  with Frame do
    begin
    for I := 0 to frHistoryIndex-1 do
      begin
      Obj := frHistory.Objects[0];
      if Assigned(Obj) and (AList.IndexOf(Obj) < 0) then
        AList.Add(Obj);
      frHistory.Delete(0);
      PositionObj(frPositionHistory[0]).Free;
      frPositionHistory.Delete(0);
      end;
    frHistoryIndex := 0;
    end;
  end;
for J := 0 to Frames.Count-1 do {now see which Objects are no longer used}
  begin
  Frame := TbrFrame(Frames[J]);
  with Frame do
    begin
    for I := 0 to frHistory.Count-1 do
      begin
      Obj := frHistory.Objects[I];
      if Assigned(Obj) and (AList.IndexOf(Obj) > -1) then
        AList.Remove(Obj);  {remove it if it's there}     
      end;
    end;
  end;
for I := 0 to AList.Count-1 do   {destroy what's left}
  TObject(AList[I]).Free;   
AList.Free;
end;

{----------------TbrFrameSet.UpdateFrameList}
procedure TbrFrameSet.UpdateFrameList;  
{Fill Frames with a list of all current TFrames}
begin
Frames.Clear;
inherited UpdateFrameList;
end;

{----------------TFrameBrowser.Create}
constructor TFrameBrowser.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Height := 150;
Width := 150;
ProcessList := TList.Create;
FLinkAttributes := TStringList.Create;  
FViewImages := True;
FBitmapList := TStringBitmapList.Create;  
FImageCacheCount := 5;
FHistory := TStringList.Create;
FPosition := TList.Create;
FTitleHistory := TStringList.Create;
FBackground := clBtnFace;
FFontColor := clBtnText;
FHotSpotColor := clBlue;
FVisitedColor := clPurple;
FOverColor := clBlue;
FVisitedMaxCount := 50;
FFontSize := 12;
FFontName := 'Times New Roman';
FPreFontName := 'Courier New';
FCursor := crIBeam;
FDither := True;
TabStop := False;
FPrintMarginLeft := 2.0;
FPrintMarginRight := 2.0;
FPrintMarginTop := 2.0;
FPrintMarginBottom := 2.0;
FPrintScale := 1.0;
FMarginWidth := 10;        
FMarginHeight := 5;        
FOptions := [fvPrintTableBackground, fvPrintMonochromeBlack];  
{$ifdef ver100_plus}  {Delphi 3,4,5, C++Builder 3, 4}
FCharset := DEFAULT_CHARSET;
{$endif}
Visited := TStringList.Create;
FEncodePostArgs := True;      

CurbrFrameSet := TbrFrameSet.Create(Self);
if fvNoBorder in FOptions then
  begin
  CurbrFrameSet.OuterBorder := 0;
  CurbrFrameSet.BevelOuter := bvNone;
  end
else
  begin
  CurbrFrameSet.OuterBorder := 2;
  CurbrFrameSet.BevelWidth := 2;
  CurbrFrameSet.BevelOuter := bvLowered;
  end;
CurbrFrameSet.Align := alClient;
InsertControl(CurbrFrameSet);
end;

{----------------TFrameBrowser.Destroy}
destructor TFrameBrowser.Destroy;
begin
ProcessList.Free;
FLinkAttributes.Free;
FHistory.Free;
FPosition.Free;
FTitleHistory.Free;
Visited.Free;
FViewerList.Free;
inherited Destroy;
FBitmapList.Free;    
end;

{----------------TFrameBrowser.Clear}
procedure TFrameBrowser.Clear;
var
  I: integer;
  Obj: TObject;
begin
if not Processing then
  begin
  for I := 0 to FHistory.Count-1 do
    with FHistory do
      begin
      Obj := Objects[0];
      Delete(0);
      if Obj <> CurbrFrameSet then
        ChkFree(Obj);
      end;
  with CurbrFrameSet do
    begin
    Clear;
    BevelOuter := bvLowered;
    BevelWidth := 2;
    end;
  FBitmapList.Clear;   
  FURL := '';
  FTarget := '';
  FBaseEx := '';
  FHistoryIndex := 0;
  FPosition.Clear;
  FTitleHistory.Clear;
  if Assigned(FOnHistoryChange) then
    FOnHistoryChange(Self);
  Visited.Clear;
  if Assigned(FViewerList) then   
    FViewerList.Clear;     
  end;
end;


{----------------TFrameBrowser.LoadURL}
procedure TFrameBrowser.LoadURL(const URL: string);
begin
if not Processing then
  begin
  LoadURLInternal(Normalize(URL), '', '', '', True, False);
  end;
end;

{----------------TFrameBrowser.GetPostQuery}
procedure TFrameBrowser.GetPostQuery(const URL, Query, EncType: string; IsGet: boolean);
begin
if not Processing then
  LoadURLInternal(Normalize(URL), Query, EncType, '', IsGet, True);
end;

{----------------TFrameBrowser.LoadURLInternal}
procedure TFrameBrowser.LoadURLInternal(const URL, Query, EncType, Referer: string;
            IsGet, Reload: boolean);
var
  OldFrameSet: TbrFrameSet;
  OldFile, S, Dest, S1: string;
  OldPos: LongInt;
  Tmp: TObject;
  SameName: boolean;
  {$ifdef Windows}
  Dummy: integer;
  {$endif}
  Stream: TMemoryStream;
  StreamType: ThtmlFileType;
  I: integer;
begin
if not Assigned(FOnGetPostRequest) and not Assigned(FOnGetPostRequestEx) then
  Raise(Exception.Create('No OnGetPostRequest or OnGetPostRequestEx event defined'));
FProcessing := True;
if Assigned(FOnProcessing) then
  FOnProcessing(Self, True);
{$ifdef windows}
Dummy :=
{$endif}
IOResult;     {remove any pending file errors}
SplitURL(URL, S, Dest);
try
  OldFile := CurbrFrameSet.FCurrentFile;
  ProcessList.Clear;
  if Assigned(FOnSoundRequest) then
    FOnSoundRequest(Self, '', 0, True);
  SameName := CompareText(OldFile, S) = 0;
  if not SameName then
    begin
    if Assigned(FOnViewerClear) then
      for I := 0 to CurbrFrameSet.Viewers.Count-1 do
        FOnViewerClear(CurbrFrameSet.Viewers[I]);
    OldFrameSet := CurbrFrameSet;
    CurbrFrameSet := TbrFrameSet.Create(Self);
          CurbrFrameSet.Align := alClient;
    CurbrFrameSet.visible := False;
    InsertControl(CurbrFrameSet);
    CurbrFrameSet.SendToBack;
    CurbrFrameSet.Visible := True;

    try
      S1 := '';
      if Assigned(FOnGetPostRequestEx) then   
        FOnGetPostRequestEx(Self, IsGet, S, Query, EncType, Referer, Reload, S1, StreamType, Stream)
      else
        FOnGetPostRequest(Self, IsGet, S, Query, Reload, S1, StreamType, Stream);
      if not Assigned(Stream) then
        Raise(EfvLoadError.Create('Can''t load: '+S));
      if S1 <> '' then
        S := S1;

      if Pos(':', S) <> 0 then
        CurbrFrameSet.URLBase := URLSubs.GetBase(S)
      else
        begin
        CurbrFrameSet.URLBase := OldFrameSet.URLBase;
        S := Combine(CurbrFrameSet.URLBase, S);
        end;

      (CurbrFrameSet as TbrFrameSet).LoadFromBrzFile(Stream, StreamType, S, Dest);
    except
      RemoveControl(CurbrFrameSet);
      CurbrFrameSet.Free;
      CurbrFrameSet := OldFrameSet;
      Raise;
      end;

    OldPos := 0;
    if (OldFrameSet.Viewers.Count = 1) then
      begin
      Tmp := OldFrameSet.Viewers[0];
      if Tmp is ThtmlViewer then
        OldPos := ThtmlViewer(Tmp).Position;
      end;
    OldFrameSet.UnloadFiles;
    CurbrFrameSet.Visible := True;
    if Visible then
      begin
      SendMessage(Handle, wm_SetRedraw, 0, 0);
      try
        CurbrFrameSet.BringToFront;
      finally
        SendMessage(Handle, wm_SetRedraw, 1, 0);
        Repaint;
        end;
      CurbrFrameSet.Repaint;
      end;

    RemoveControl(OldFrameSet);
    BumpHistory(OldFrameSet, OldPos);
    end
  else
    begin    {Same name}
    OldPos := 0;
    if (CurbrFrameSet.Viewers.Count = 1) then
      begin
      Tmp := CurbrFrameSet.Viewers[0];
      if Tmp is ThtmlViewer then
        OldPos := ThtmlViewer(Tmp).Position;
      end;
    if Assigned(FOnGetPostRequestEx) then   
      FOnGetPostRequestEx(Self, IsGet, S, Query, EncType, Referer, Reload, S1, StreamType, Stream)
    else
      FOnGetPostRequest(Self, IsGet, S, Query, Reload, S1, StreamType, Stream);  
    if not Assigned(Stream) then
      Raise(EfvLoadError.Create('Can''t locate cache file: '+S));

    if S1 <> '' then
      begin
      S := S1;
      if Pos(':', S) <> 0 then
        CurbrFrameSet.URLBase := URLSubs.GetBase(S);
      end;

    (CurbrFrameSet as TbrFrameSet).LoadFromBrzFile(Stream, StreamType, S, Dest);
    BumpHistory2(OldPos);   {not executed if exception occurs}
    end;
  AddVisitedLink(URL);
finally
  FProcessing := False;
  if Assigned(FOnProcessing) then
    FOnProcessing(Self, False);
  end;
end;

{----------------TFrameBrowser.Reload}
procedure TFrameBrowser.Reload;
begin
FProcessing := True;
if Assigned(FOnProcessing) then
  FOnProcessing(Self, True);
try
  ProcessList.Clear;
  SendMessage(Self.handle, wm_SetRedraw, 0, 0);
  try
    CurbrFrameSet.UnloadFiles;
    CurbrFrameSet.ReloadFiles(-1);
  finally
    SendMessage(Self.handle, wm_SetRedraw, 1, 0);
    end;
  CheckVisitedLinks;    
finally
  FProcessing := False;
  if Assigned(FOnProcessing) then
    FOnProcessing(Self, False);
  end;
end;

{----------------TFrameBrowser.GetFwdButtonEnabled}
function TFrameBrowser.GetFwdButtonEnabled: boolean;
var
  I: integer;
  Frame: TbrFrame;
begin
Result := fHistoryIndex >= 1;
if not Result then
  for I := 0 to CurbrFrameSet.Frames.Count-1 do
    begin
    Frame := TbrFrame(CurbrFrameSet.Frames[I]);
    with Frame do
      if frHistoryIndex >= 1 then
        begin
        Result := True;
        Exit;
        end;
    end;
end;

{----------------TFrameBrowser.GetBackButtonEnabled}
function TFrameBrowser.GetBackButtonEnabled: boolean;
var
  I: integer;
  Frame: TbrFrame;
begin
Result := fHistoryIndex <= fHistory.Count-2;
if not Result then
  for I := 0 to CurbrFrameSet.Frames.Count-1 do
    begin
    Frame := TbrFrame(CurbrFrameSet.Frames[I]);
    with Frame do
      if frHistoryIndex <= frHistory.Count-2 then
        begin
        Result := True;
        Exit;
        end;
    end;
end;

procedure TFrameBrowser.GoFwd;
var
  I, Smallest, Index: integer;
  Frame, TheFrame: TbrFrame;
begin
Smallest := 9999;
Index := 0; TheFrame := Nil;   {to quiet the warnings}
for I := 0 to CurbrFrameSet.Frames.Count-1 do
  begin
  Frame := TbrFrame(CurbrFrameSet.Frames[I]);
  with Frame do
    if frHistoryIndex >= 1 then
      with PositionObj(frPositionHistory[frHistoryIndex-1]) do
        if Seq < Smallest then
          begin
          Smallest := Seq;
          TheFrame := Frame;
          Index := frHistoryIndex;
          end;
  end;
if Smallest < 9999 then
  TheFrame.frSetHistoryIndex(Index - 1)
else SetHistoryIndex(fHistoryIndex - 1);
if Assigned(FOnSoundRequest) then
  FOnSoundRequest(Self, '', 0, True);
end;

procedure TFrameBrowser.GoBack;
var
  I, Largest, Index: integer;
  Frame, TheFrame: TbrFrame;
begin
Largest := -1;
Index := 0; TheFrame := Nil;   {to quiet the warnings}
for I := 0 to CurbrFrameSet.Frames.Count-1 do
  begin
  Frame := TbrFrame(CurbrFrameSet.Frames[I]);
  with Frame do
    if frHistoryIndex <= frHistory.Count-2 then
      with PositionObj(frPositionHistory[frHistoryIndex]) do
        if Seq > Largest then
          begin
          Largest := Seq;
          TheFrame := Frame;
          Index := frHistoryIndex;
          end;
  end;
if Largest >= 0 then
  TheFrame.frSetHistoryIndex(Index + 1)
else
  SetHistoryIndex(fHistoryIndex+1);
if Assigned(FOnSoundRequest) then
  FOnSoundRequest(Self, '', 0, True);
end;

{----------------TFrameBrowser.HotSpotClickHandled:}
function TFrameBrowser.HotSpotClickHandled(const FullUrl: string): boolean;
var
  Handled: boolean;
begin
Handled := False;
if Assigned(FOnHotSpotTargetClick) then
  FOnHotSpotTargetClick(Self, FTarget, FullUrl, Handled);
Result := Handled;
end;

{----------------TFrameBrowser.HotSpotClick}
procedure TFrameBrowser.HotSpotClick(Sender: TObject; const AnURL: string;
          var Handled: boolean);
var
  I: integer;
  Viewer: ThtmlViewer;
  FrameTarget: TbrFrameBase;
  S, Dest, FullUrl: string;

begin
if Processing then
  begin
  Handled := True;
  Exit;
  end;
Viewer := (Sender as ThtmlViewer);
FURL := AnURL;
FTarget := GetActiveTarget;
FLinkAttributes.Text := Viewer.LinkAttributes.Text;  
FLinkText := Viewer.LinkText;
SplitUrl(AnUrl, S, Dest);
S := ConvDosToHTML(S);
if S = '' then
  FullUrl := (Viewer.FrameOwner as TbrFrame).Source
else if IsFullURL(S) then
  FullUrl := S
else if Viewer.Base <> '' then      
  FullUrl := Combine(UrlSubs.GetBase(ConvDosToHTML(Viewer.Base)), S)
else
  FullUrl := Combine((Viewer.FrameOwner as TbrFrame).URLBase, S);

Handled := HotSpotClickHandled(FullUrl + Dest);

if not Handled then
  begin
  Handled := True;
  if (FTarget = '') or (CompareText(FTarget, '_self') = 0) then  {no target or _self target}
    begin
    FrameTarget := Viewer.FrameOwner as TbrFrame;
    if not Assigned(FrameTarget) then Exit;
    end
  else if CurbrFrameSet.FrameNames.Find(FTarget, I) then
    FrameTarget := (CurbrFrameSet.FrameNames.Objects[I] as TbrFrame)
  else if CompareText(FTarget, '_top') = 0 then
    FrameTarget := CurbrFrameSet
  else if CompareText(FTarget, '_parent') = 0 then
    begin
    FrameTarget := (Viewer.FrameOwner as TbrFrame).Owner as TbrFrameBase;
    while Assigned(FrameTarget) and not (FrameTarget is TbrFrame)
              and not (FrameTarget is TbrFrameSet) do
       FrameTarget := FrameTarget.Owner as TbrFrameBase;
    end
  else
    begin
    if Assigned(FOnBlankWindowRequest) then
      begin
      AddVisitedLink(FullUrl + Dest);
      CheckVisitedLinks;
      FOnBlankWindowRequest(Self, FTarget, FullUrl + Dest);
      Handled := True;
      end
    else Handled := FTarget <> '';   {true if can't find target window}
    Exit;
    end;

  FProcessing := True;
  if Assigned(FOnProcessing) then
    FOnProcessing(Self, True);
  if (FrameTarget is TbrFrame) and (CurbrFrameSet.Viewers.Count = 1) and (S <> '')
        and (CompareText(S, CurbrFrameSet.FCurrentFile) <> 0) then
    FrameTarget := CurbrFrameSet;  {force a new FrameSet on name change}
  try
    if FrameTarget is TbrFrame then
      TbrFrame(FrameTarget).frLoadFromBrzFile(FullUrl, Dest, '', '', Viewer.CurrentFile, True, True, False) 
    else if FrameTarget is TbrFrameSet then
      Self.LoadURLInternal(FullUrl + Dest, '', '', Viewer.CurrentFile, True, False);
    CheckVisitedLinks;    
  finally
    FProcessing := False;     {changed position}
    if Assigned(FOnProcessing) then
      FOnProcessing(Self, False);
    end;
  end;
end;

function TFrameBrowser.GetCurViewerCount: integer;
begin
   Result := CurbrFrameSet.Viewers.Count;
end;

function TFrameBrowser.GetCurViewer(I: integer): ThtmlViewer;
begin
   Result := CurbrFrameSet.Viewers[I];
end;

{----------------TFrameBrowser.HotSpotCovered}
procedure TFrameBrowser.HotSpotCovered(Sender: TObject; const SRC: string);
var
  S, Dest, FullUrl: string;
  Viewer: ThtmlViewer;
begin
if Assigned(FOnHotSpotTargetCovered) then
  begin
  Viewer := Sender as ThtmlViewer;   
  SplitUrl(SRC, S, Dest);
  S := ConvDosToHTML(S);    {convert DOS names}
  if IsFullURL(S) or (Src = '') then
    FullUrl := S
  else
    begin
    if Viewer.Base <> '' then
      FullUrl := Combine(UrlSubs.GetBase(ConvDosToHTML(Viewer.Base)), S)
    else
      FullUrl := Combine((Viewer.FrameOwner as TbrFrame).URLBase, S);  
    end;
  FLinkText := Viewer.LinkText; 
  FLinkAttributes.Text := Viewer.LinkAttributes.Text;
  FOnHotSpotTargetCovered(Sender, (Sender as ThtmlViewer).Target, FullUrl+Dest);
  end;
end;

{----------------TFrameBrowser.GetActiveTarget}
function TFrameBrowser.GetActiveTarget: string;
var
   Vw: ThtmlViewer;
   Done: boolean;
   FSet: TbrSubFrameSet;
begin          
Result := '';
Vw := GetActiveViewer;
if Assigned(Vw) then
  begin
  Result := Vw.Target;
  if Result = '' then Result := Vw.BaseTarget;
  Done := False;
  FSet := TbrFrame(Vw.FrameOwner).LOwner;
  while (Result = '') and Assigned(FSet) and not Done do
    begin
    Result := FSet.FBaseTarget;
    Done := FSet = CurbrFrameSet;
    if not Done then FSet := FSet.LOwner;
    end;
  end;
end;

function TFrameBrowser.GetBase: string;
begin
Result := CurbrFrameSet.FBase;
end;

procedure TFrameBrowser.SetBase(Value: string);
begin
CurbrFrameSet.FBase := Value;
FBaseEx := Value;
end;

function TFrameBrowser.GetBaseTarget: string;
begin
Result := CurbrFrameSet.FBaseTarget;
end;

function TFrameBrowser.GetTitle: string;
begin
Result := CurbrFrameSet.FTitle;
end;

function TFrameBrowser.GetCurrentFile: string;
begin
Result := CurbrFrameSet.FCurrentFile;
end;

{----------------TFrameBrowser.GetActiveViewer}
function TFrameBrowser.GetActiveViewer: ThtmlViewer;
begin
Result := CurbrFrameSet.GetActive;
end;

{----------------TFrameBrowser.BumpHistory}
procedure TFrameBrowser.BumpHistory(OldFrameSet: TbrFrameSet; OldPos: LongInt);
{OldFrameSet never equals CurbrFrameSet when this method called}
var
  I: integer;
  Obj: TObject;
begin
if (FHistoryMaxCount > 0) and (CurbrFrameSet.FCurrentFile <> '') then
  with FHistory do
    begin
    if (Count > 0) then
      begin
      Strings[FHistoryIndex] := OldFrameSet.FCurrentFile;
      Objects[FHistoryIndex] := OldFrameSet;
      FTitleHistory[FHistoryIndex] := OldFrameSet.FTitle;
      FPosition[FHistoryIndex] := TObject(OldPos);
      OldFrameSet.ClearForwards;
      end
    else OldFrameSet.Free;
    for I := 0 to FHistoryIndex-1 do
      begin
      Obj := Objects[0];
      Delete(0);
      ChkFree(Obj);
      FTitleHistory.Delete(0);
      FPosition.Delete(0);
      end;
    FHistoryIndex := 0;
    Insert(0, CurbrFrameSet.FCurrentFile);
    Objects[0] := CurbrFrameSet;
    FTitleHistory.Insert(0, CurbrFrameSet.FTitle);
    FPosition.Insert(0, Nil);
    if Count > FHistoryMaxCount then
      begin
      Obj := Objects[FHistoryMaxCount];
      Delete(FHistoryMaxCount);
      ChkFree(Obj);
      FTitleHistory.Delete(FHistoryMaxCount);
      FPosition.Delete(FHistoryMaxCount);
      end;
    if Assigned(FOnHistoryChange) then FOnHistoryChange(Self);
    end
else OldFrameSet.Free;
end;

{----------------TFrameBrowser.BumpHistory1}
procedure TFrameBrowser.BumpHistory1(const FileName, Title: string;
                 OldPos: LongInt; ft: ThtmlFileType);
{This variation called when CurbrFrameSet contains only a single viewer before
 and after the change}
var
  I: integer;
  Obj: TObject;
begin
if (FHistoryMaxCount > 0) and (Filename <> '') then
  with FHistory do
    begin
    if (Count > 0) then
      begin
      Strings[FHistoryIndex] := Filename;
      Objects[FHistoryIndex] := CurbrFrameSet;
      FTitleHistory[FHistoryIndex] := Title;
      FPosition[FHistoryIndex] := TObject(OldPos);
      end;
    for I := 0 to FHistoryIndex-1 do
      begin
      Obj := Objects[0];
      Delete(0);
      ChkFree(Obj);
      FTitleHistory.Delete(0);
      FPosition.Delete(0);
      end;
    FHistoryIndex := 0;
    Insert(0, CurbrFrameSet.FCurrentFile);
    Objects[0] := CurbrFrameSet;
    FTitleHistory.Insert(0, CurbrFrameSet.FTitle);
    FPosition.Insert(0, Nil);
    if Count > FHistoryMaxCount then
      begin
      Obj := Objects[FHistoryMaxCount];
      Delete(FHistoryMaxCount);
      ChkFree(Obj);
      FTitleHistory.Delete(FHistoryMaxCount);
      FPosition.Delete(FHistoryMaxCount);
      end;
    if Assigned(FOnHistoryChange) then FOnHistoryChange(Self);
    end;
end;

{----------------TFrameBrowser.BumpHistory2}
procedure TFrameBrowser.BumpHistory2(OldPos: LongInt);
{CurbrFrameSet has not changed when this method called}
var
  I: integer;
  Obj: TObject;
begin
if (FHistoryMaxCount > 0) and (CurbrFrameSet.FCurrentFile <> '') then
  with FHistory do
    begin
    if (Count > 0) then
      begin
      Strings[FHistoryIndex] := CurbrFrameSet.FCurrentFile;
      Objects[FHistoryIndex] := CurbrFrameSet;
      FTitleHistory[FHistoryIndex] := CurbrFrameSet.FTitle;
      FPosition[FHistoryIndex] := TObject(OldPos);
      end;
    for I := 0 to FHistoryIndex-1 do
      begin
      Obj := Objects[0];
      Delete(0);
      ChkFree(Obj);
      FTitleHistory.Delete(0);
      FPosition.Delete(0);
      end;
    FHistoryIndex := 0;
    Insert(0, CurbrFrameSet.FCurrentFile);
    Objects[0] := CurbrFrameSet;
    FTitleHistory.Insert(0, CurbrFrameSet.FTitle);
    FPosition.Insert(0, Nil);
    if Count > FHistoryMaxCount then
      begin
      Obj := Objects[FHistoryMaxCount];
      Delete(FHistoryMaxCount);
      ChkFree(Obj);
      FTitleHistory.Delete(FHistoryMaxCount);
      FPosition.Delete(FHistoryMaxCount);
      end;
    if Assigned(FOnHistoryChange) then FOnHistoryChange(Self);
    end;
end;

{----------------TFrameBrowser.SetHistoryIndex}
procedure TFrameBrowser.SetHistoryIndex(Value: integer);
var
  FrameSet, FrameSet1: TbrFrameSet;  
  Tmp: TObject;
begin
with CurbrFrameSet, FHistory do
  if (Value <> FHistoryIndex) and (Value >= 0) and (Value < Count)
            and not Processing then
    begin
    if CurbrFrameSet.Viewers.Count > 0 then
      Tmp := CurbrFrameSet.Viewers[0]
    else Tmp := Nil;
    if FCurrentFile <> '' then
      begin
      {Objects[FHistoryIndex] should have CurbrFrameSet here}
      FTitleHistory[FHistoryIndex] := CurbrFrameSet.FTitle;
      if (Tmp is ThtmlViewer) then
        FPosition[FHistoryIndex] := TObject((Tmp as ThtmlViewer).Position)
      else  FPosition[FHistoryIndex] := Nil;
      end;
    FrameSet := Objects[Value] as TbrFrameSet;
    if FrameSet <> CurbrFrameSet then
      begin
      FrameSet1 := CurbrFrameSet;   {swap framesets} 
      CurbrFrameSet := FrameSet;
      CurbrFrameSet.OldWidth := 0;    {encourage recalc of internal layout}
      CurbrFrameSet.Visible := False;
      Self.InsertControl(CurbrFrameSet);
      if CurbrFrameSet.Viewers.Count = 1 then
        CurbrFrameSet.ReloadFiles(LongInt(FPosition[Value]))
      else
        CurbrFrameSet.ReloadFiles(-1);
      SendMessage(Self.handle, wm_SetRedraw, 0, 0);
      CurbrFrameSet.Visible := True;
      SendMessage(Self.handle, wm_SetRedraw, 1, 0);
      CurbrFrameSet.Repaint;
      FrameSet1.Unloadfiles;
      Self.RemoveControl(FrameSet1);
      end
    else
      begin
      if  (Tmp is ThtmlViewer) then
        TbrFrame(ThtmlViewer(Tmp).FrameOwner).ReloadFile(FHistory[Value],
                          LongInt(FPosition[Value]));
      end;

    FHistoryIndex := Value;
    if Assigned(FOnHistoryChange) then FOnHistoryChange(Self);
    CheckVisitedLinks;
    end;
end;

{----------------TFrameBrowser.ChkFree}
procedure TFrameBrowser.ChkFree(Obj: TObject);
{Frees a TbrFrameSet only if it no longer exists in FHistory}
var
  I: integer;
begin
for I := 0 to FHistory.Count-1 do
  if Obj = FHistory.Objects[I] then Exit;
(Obj as TbrFrameSet).Free;
end;

{----------------TFrameBrowser.ClearHistory}
procedure TFrameBrowser.ClearHistory;
var
  I: integer;
  Obj: TObject;
  DidSomething: boolean;
begin
DidSomething := FHistory.Count > 0;
for I := FHistory.Count-1 downto 0 do
  begin
  Obj := FHistory.Objects[I];
  FHistory.Delete(I);
  if Obj <> CurbrFrameSet then
    ChkFree(Obj);
  end;
if Assigned(CurbrFrameSet) then
  for I := 0 to CurbrFrameSet.Frames.Count-1 do
    with TbrFrame(CurbrFrameSet.Frames[I]) do
      begin
      DidSomething := DidSomething or (frHistory.Count > 0);
      frHistoryIndex := 0;
      frHistory.Clear;
      frPositionHistory.Clear;
      end;
FHistory.Clear;
FTitleHistory.Clear;
FPosition.Clear;
FHistoryIndex := 0;
if DidSomething and Assigned(FOnHistoryChange) then
  FOnHistoryChange(Self);
end;

function TFrameBrowser.ViewerFromTarget(const Target: string): ThtmlViewer;
var
  I: integer;
begin
if Assigned(CurbrFrameSet) and Assigned(CurbrFrameSet.FrameNames)
     and CurbrFrameSet.FrameNames.Find(Target, I)
     and (CurbrFrameSet.FrameNames.Objects[I] <> Nil)
     and Assigned((CurbrFrameSet.FrameNames.Objects[I] as TbrFrame).Viewer) then
  Result := TbrFrame(CurbrFrameSet.FrameNames.Objects[I]).Viewer as ThtmlViewer
else Result := Nil;
end;

procedure TFrameBrowser.RePaint;
begin
if Assigned(CurbrFrameSet) then
  CurbrFrameSet.RePaint;
end;

procedure TFrameBrowser.SetOptions(Value: TFrameViewerOptions);
var
  I: integer;
begin
if (fvNoBorder in FOptions) <> (fvNoBorder in Value) then
  if fvNoBorder in Value then
    begin                  
    CurbrFrameSet.OuterBorder := 0;
    CurbrFrameSet.BevelOuter := bvNone;
    end
  else
    begin
    CurbrFrameSet.OuterBorder := 2;
    CurbrFrameSet.BevelWidth := 2;
    CurbrFrameSet.BevelOuter := bvLowered;
    end;
for I := 0 to CurbrFrameSet.Viewers.Count-1 do
  with ThtmlViewer(CurbrFrameSet.Viewers[I]) do
    begin
    if (fvOverLinksActive in Value) then
      htOptions := htOptions + [htOverLinksActive]
    else htOptions := htOptions - [htOverLinksActive];

    if (fvNoLinkUnderline in Value) then
      htOptions := htOptions + [htNoLinkUnderline]
    else htOptions := htOptions - [htNoLinkUnderline];

    if (fvPrintTableBackground in Value) then
      htOptions := htOptions + [htPrintTableBackground]
    else htOptions := htOptions - [htPrintTableBackground];

    if (fvPrintBackground in Value) then           
      htOptions := htOptions + [htPrintBackground]
    else htOptions := htOptions - [htPrintBackground];

    if (fvPrintMonochromeBlack in Value) then
      htOptions := htOptions + [htPrintMonochromeBlack]
    else htOptions := htOptions - [htPrintMonochromeBlack];

    if (fvShowVScroll in Value) then
      htOptions := htOptions + [htShowVScroll]
    else htOptions := htOptions - [htShowVScroll];

    if (fvNoWheelMouse in Value) then
      htOptions := htOptions + [htNoWheelMouse]
    else htOptions := htOptions - [htNoWheelMouse];

    if (fvNoFocusRect in Value) or (fvNoBorder in Value) then
      BorderStyle := htNone
    else BorderStyle := htFocused;
    end;
FOptions := Value;
end;

procedure TFrameBrowser.AddFrame(FrameSet: TObject; Attr: TAttributeList; const FName: string);
begin
(FrameSet as TbrSubFrameSet).AddFrame(Attr, FName);
end;

function TFrameBrowser.CreateSubFrameSet(FrameSet: TObject): TObject;
var
  NewFrameSet, FS: TbrSubFrameSet;
begin
FS := (FrameSet as TbrSubFrameSet);
NewFrameSet := TbrSubFrameSet.CreateIt(FS, CurbrFrameSet);
FS.List.Add(NewFrameSet);
FS.InsertControl(NewFrameSet);
Result := NewFrameSet;
end;

procedure TFrameBrowser.DoAttributes(FrameSet: TObject; Attr: TAttributeList);
begin
(FrameSet as TbrSubFrameSet).DoAttributes(Attr);
end;

procedure TFrameBrowser.EndFrameSet(FrameSet: TObject);
begin
(FrameSet as TbrSubFrameSet).EndFrameSet;
end;

procedure TFrameBrowser.SetOnProgress(Handler: ThtProgressEvent);  
var
  I: integer;
begin
FOnProgress := Handler;
with CurbrFrameSet do
  for I := 0 to Viewers.Count-1 do
    with ThtmlViewer(Viewers[I]) do
      OnProgress := Handler;
end;

procedure TFrameBrowser.SetDragDrop(const Value: TDragDropEvent);
var
  I: integer;
begin
FOnDragDrop := Value;
if Assigned(CurbrFrameSet) then
  if Assigned(Value) then
    CurbrFrameSet.OnDragDrop := fvDragDrop
  else CurbrFrameSet.OnDragDrop := Nil;
for I := 0 to GetCurViewerCount-1 do
  if Assigned(Value) then
    CurViewer[I].OnDragDrop := fvDragDrop
  else CurViewer[I].OnDragDrop := Nil;
end;

procedure TFrameBrowser.fvDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
if Assigned(FOnDragDrop) then
  FOnDragDrop(Self, Source, X, Y);
end;

procedure TFrameBrowser.SetDragOver(const Value: TDragOverEvent); 
var
  I: integer;
begin
FOnDragOver := Value;
if Assigned(CurbrFrameSet) then
  if Assigned(Value) then
    CurbrFrameSet.OnDragOver := fvDragOver
  else CurbrFrameSet.OnDragOver := Nil;
for I := 0 to GetCurViewerCount-1 do
  if Assigned(Value) then
    CurViewer[I].OnDragOver := fvDragOver
  else CurViewer[I].OnDragOver := Nil;
end;

procedure TFrameBrowser.fvDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
if Assigned(FOnDragOver) then
  FOnDragOver(Self, Source, X, Y, State, Accept);
end;

{----------------TFrameBrowser.SetOnImageRequest}
procedure TFrameBrowser.SetOnImageRequest(const Value: TGetImageEvent);
var
  I: integer;
begin
FOnImageRequest := Value;
with CurbrFrameSet do
  for I := 0 to Viewers.Count-1 do
    with ThtmlViewer(Viewers[I]) do
      OnImageRequest := Value;
end;

{----------------TFrameBrowser.DoFormSubmitEvent}
procedure TFrameBrowser.DoFormSubmitEvent(Sender: TObject; const Action, Target, EncType,
  Method: string; Results: TStringList);
var
  S, Dest, Query: string;
  FrameTarget: TbrFrameBase;
  I: integer;
  Viewer: ThtmlViewer;
  UserHandled, IsGet: boolean;

  function AssembleQuery: string;
  var
    S1: string;
    I, J: integer;

    function Encode(const S: string): string;
    var
      Ch: char;
      I: integer;
    begin   {convert odd chars into %xx -- does not handle the '=' sign yet}
    Result := '';
    for I := 1 to Length(S) do
      begin
      Ch := S[I];
      if Ch = ' ' then Result := Result+'+'
      else if not (Ch in ['a'..'z', 'A'..'Z', '0'..'9', '=', '_','-','.','*','@']) then
         Result := Result+'%'+IntToHex(ord(Ch),2)
      else Result := Result+Ch;
      end;
    end;

  begin
  Result := '';   
  for I := 0 to Results.Count-1 do
    begin
    if FEncodePostArgs then
      begin {form a string from the TStringList using '+' for spaces and '&' for separaters}
      S1 := Encode(Results[I]);
      J := Pos(' ', S1);
      while J > 0 do
        begin
        S1[J] := '+';
        J := Pos(' ', S1);
        end;
      end
    else S1 := Trim(Results[I]); {No encoding done}
    if I <> 0 then
      Result := Result + '&';  
    Result := Result + S1;
    end;
  Results.Free;
  end;

begin
if InFormSubmit then
  Exit;
InFormSubmit := True;   
try
  {see if the application wants to handle this event}
  UserHandled := false;
  Viewer := (Sender as ThtmlViewer);  
  if Assigned(FOnFormSubmit) then
     FOnFormSubmit(Self, Viewer, Action, Target, EncType, Method, Results, UserHandled);
  if not UserHandled then
    begin
    Query := AssembleQuery;

    if (Target = '') or (CompareText(Target, '_self') = 0) then  {no target or _self target}
      FrameTarget := Viewer.FrameOwner as TbrFrame
    else if CurbrFrameSet.FrameNames.Find(Target, I) then
      FrameTarget := (CurbrFrameSet.FrameNames.Objects[I] as TbrFrame)
    else if CompareText(Target, '_top') = 0 then
      FrameTarget := CurbrFrameSet
    else if CompareText(Target, '_parent') = 0 then
      begin
      FrameTarget := (Viewer.FrameOwner as TbrFrame).Owner as TbrFrameBase;
      while Assigned(FrameTarget) and not (FrameTarget is TbrFrame)
                and not (FrameTarget is TbrFrameSet) do
         FrameTarget := FrameTarget.Owner as TbrFrameBase;
      end
    else
      begin
      if Assigned(FOnBlankWindowRequest) then
        FOnBlankWindowRequest(Self, Target, Action+'?'+Query);
      Exit;
      end;

    S := Action;
    I := Pos('#', S);
    if I >= 1 then
      begin
      Dest := System.Copy(S, I, Length(S)-I+1);  {local destination}
      S := System.Copy(S, 1, I-1);     {the file name}
      end
    else
      Dest := '';    {no local destination}

    FProcessing := True;
    if Assigned(FOnProcessing) then
      FOnProcessing(Self, True);
    if (FrameTarget is TbrFrame) and (CurbrFrameSet.Viewers.Count = 1) and (S <> '')
          and (CompareText(S, CurbrFrameSet.FCurrentFile) <> 0) then
      FrameTarget := CurbrFrameSet;  {force a new FrameSet on name change}
    try
      if S = '' then
        S := (Viewer.FrameOwner as TbrFrame).Source
      else if not IsFullURL(S) then
        S := Combine((Viewer.FrameOwner as TbrFrame).URLBase, S);
      IsGet := CompareText(Method, 'get') = 0;
      if FrameTarget is TbrFrame then
        TbrFrame(FrameTarget).frLoadFromBrzFile(S, Dest, Query, EncType,
               Viewer.CurrentFile, True, IsGet, True)
      else if FrameTarget is TbrFrameSet then
        Self.LoadURLInternal(S + Dest, Query, EncType, Viewer.CurrentFile, IsGet, True);
    finally
      FProcessing := False;
      if Assigned(FOnProcessing) then
        FOnProcessing(Self, False);
      end;
    end;
finally
  InFormSubmit := False;
  end;
end;

procedure TFrameBrowser.DoURLRequest(Sender: TObject; const SRC: string; var Stream: TMemoryStream);
var
  NewURL: string;
  DocType: ThtmlFileType;
begin
if Assigned(FOnGetPostRequestEx) then
  FOnGetPostRequestEx(Sender, True, SRC, '', '', '', False, NewURL, DocType, Stream)
else if Assigned(FOnGetPostRequest) then
  FOnGetPostRequest(Sender, True, SRC, '', False, NewURL, DocType, Stream);
end;

{----------------TFrameBrowser.GetViewerUrlBase}
function TFrameBrowser.GetViewerUrlBase(Viewer: ThtmlViewer): string;
var
  Frame: TbrFrame;
begin
try
  Frame := (Viewer as ThtmlViewer).FrameOwner as TbrFrame;
  Result := Frame.UrlBase;
except
  Result := '';
  end;
end;

{----------------TFrameBrowser.AddVisitedLink}
procedure TFrameBrowser.AddVisitedLink(const S: string);
var
  I: integer;
begin
if (FVisitedMaxCount = 0) then
  Exit;
I := Visited.IndexOf(S);
if I = 0 then
  Exit
else if I > 0 then
  Visited.Delete(I);   {thus moving it to the top}
Visited.Insert(0, S);
for I :=  Visited.Count-1 downto FVisitedMaxCount do
  Visited.Delete(I);
end;

{----------------TFrameBrowser.CheckVisitedLinks}
procedure TFrameBrowser.CheckVisitedLinks;
var
  I, J, K: integer;
  S, S1: string;
  Viewer: ThtmlViewer;
begin
if FVisitedMaxCount = 0 then
  Exit;
for K := 0 to CurbrFrameSet.Viewers.Count-1 do
  begin
  Viewer := ThtmlViewer(CurbrFrameSet.Viewers[K]);
  for I := 0 to Visited.Count-1 do
    begin
    S := Visited[I];
    for J := 0 to Viewer.LinkList.Count-1 do
      with TFontObj(Viewer.LinkList[J]) do
        begin
        if Url <> '' then
          begin
          if IsFullURL(Url) then
            S1 := Url
          else if Url[1] = '#' then
            S1 :=  TbrFrame(Viewer.FrameOwner).Source+Url
          else S1 := Combine(TbrFrame(Viewer.FrameOwner).UrlBase, Url);
          if CompareText(S, S1) = 0 then
            Visited := True;
          end;
        end;
    end;
  Viewer.Invalidate;
  end;
end;

{----------------TFrameBrowser.GetViewers}
function TFrameBrowser.GetViewers: TStrings;   
var
  I: integer;
  S: string;
  AFrame: TbrFrame;
  Viewer: ThtmlViewer;
  Pt1, Pt2: TPoint;
begin
if not Assigned(FViewerList) then
  FViewerList := TStringList.Create
else FViewerList.Clear;
for I := 0 to CurbrFrameSet.Viewers.Count-1 do
  begin
  Viewer := CurbrFrameSet.Viewers[I];
  if Viewer.SectionList.Count > 0  then
    begin
    S := '';
    AFrame := TbrFrame(Viewer.FrameOwner);
    Pt1 := AFrame.ClientToScreen(Point(0,0));
    Pt2 := CurbrFrameSet.ClientToScreen(Point(0,0));
    if Pt1.X <= Pt2.X +2 then
      S := S+'l';
    if Pt1.Y <= Pt2.Y +2 then
      S := S+'t';
    Pt1 := AFrame.ClientToScreen(Point(AFrame.ClientWidth, AFrame.ClientHeight));
    Pt2 := CurbrFrameSet.ClientToScreen(Point(CurbrFrameSet.ClientWidth, CurbrFrameSet.ClientHeight));
    if Pt1.X >= Pt2.X -2 then
      S := S+'r';
    if Pt1.Y >= Pt2.Y -2 then
      S := S+'b';
    FViewerList.AddObject(S, Viewer);
    end;
  end;
Result := FViewerList;
end;

{----------------PositionObj}
destructor PositionObj.Destroy;
begin
FormData.Free;
inherited;
end;

end.
